source("scr/libraries.R")
source("scr/ggplot.R")
source("scr/VCFfilterstats.R")
source("scr/HaplotypR.R")
source("scr/xtrafunctions.R")
source("scr/genind.R")

Receiving and demultiplexing libraries

Demultiplex samples

Make sure index is in the file name of the downloaded sequences before demultiplexing; input file barcode then index; demultiplex.txt is in UNIX format.

Demultiplex CLE-2

Demultiplex using only single enzyme.

Demultiplex CLE-3

Demultiplex using only single enzyme.

Quality control demultiplexed reads

Parse process radtags log file.

# if already run unattach list
rm(l)
# create empty list
l <- list()
# CLE-2 ----
# number of samples in index
index <- "CGATGT"
lib <- "CLE2"
n_samples <- 29
# read in summary per barcode
l[["CLE2-2"]] <- read_table2("data/SEQ/CLE2_CGATGT_radtags.log",
            skip = 13, n_max = n_samples,
            col_names = c("BARCODE", "TOTAL_READS", "AMBIG_READS", "LQ_READS", "RETAINED")) %>%
  mutate(PROP_RETAINED = RETAINED/TOTAL_READS,
         INDEX = index,
         LIBRARY = lib) %>%
  select(LIBRARY, INDEX, BARCODE, PROP_RETAINED, TOTAL_READS, RETAINED, AMBIG_READS, LQ_READS)
# number of samples in index
index <- "TGACCA"
lib <- "CLE2"
n_samples <- 29
# read in summary per barcode
l[["CLE2-4"]] <- read_table2("data/SEQ/CLE2_TGACCA_radtags.log",
            skip = 13, n_max = n_samples,
            col_names = c("BARCODE", "TOTAL_READS", "AMBIG_READS", "LQ_READS", "RETAINED")) %>%
  mutate(PROP_RETAINED = RETAINED/TOTAL_READS,
         INDEX = index,
         LIBRARY = lib) %>%
  select(LIBRARY, INDEX, BARCODE, PROP_RETAINED, TOTAL_READS, RETAINED, AMBIG_READS, LQ_READS)
# number of samples in index
index <- "CAGATC"
lib <- "CLE2"
n_samples <- 29
# read in summary per barcode
l[["CLE2-7"]] <- read_table2("data/SEQ/CLE2_CAGATC_radtags.log",
            skip = 13, n_max = n_samples,
            col_names = c("BARCODE", "TOTAL_READS", "AMBIG_READS", "LQ_READS", "RETAINED")) %>%
  mutate(PROP_RETAINED = RETAINED/TOTAL_READS,
         INDEX = index,
         LIBRARY = lib) %>%
  select(LIBRARY, INDEX, BARCODE, PROP_RETAINED, TOTAL_READS, RETAINED, AMBIG_READS, LQ_READS)
# number of samples in index
index <- "TAGCTT"
lib <- "CLE2"
n_samples <- 29
# read in summary per barcode
l[["CLE2-10"]] <- read_table2("data/SEQ/CLE2_TAGCTT_radtags.log",
            skip = 13, n_max = n_samples,
            col_names = c("BARCODE", "TOTAL_READS", "AMBIG_READS", "LQ_READS", "RETAINED")) %>%
  mutate(PROP_RETAINED = RETAINED/TOTAL_READS,
         INDEX = index,
         LIBRARY = lib) %>%
  select(LIBRARY, INDEX, BARCODE, PROP_RETAINED, TOTAL_READS, RETAINED, AMBIG_READS, LQ_READS)
# CLE-3 ----
# number of samples in index
index <- "CGATGT"
lib <- "CLE2"
n_samples <- 29
# read in summary per barcode
l[["CLE3-2"]] <- read_table2("data/SEQ/CLE3_CGATGT_radtags.log",
            skip = 13, n_max = n_samples,
            col_names = c("BARCODE", "TOTAL_READS", "AMBIG_READS", "LQ_READS", "RETAINED")) %>%
  mutate(PROP_RETAINED = RETAINED/TOTAL_READS,
         INDEX = index,
         LIBRARY = lib) %>%
  select(LIBRARY, INDEX, BARCODE, PROP_RETAINED, TOTAL_READS, RETAINED, AMBIG_READS, LQ_READS)
# number of samples in index
index <- "TGACCA"
lib <- "CLE3"
n_samples <- 29
# read in summary per barcode
l[["CLE3-4"]] <- read_table2("data/SEQ/CLE3_TGACCA_radtags.log",
            skip = 13, n_max = n_samples,
            col_names = c("BARCODE", "TOTAL_READS", "AMBIG_READS", "LQ_READS", "RETAINED")) %>%
  mutate(PROP_RETAINED = RETAINED/TOTAL_READS,
         INDEX = index,
         LIBRARY = lib) %>%
  select(LIBRARY, INDEX, BARCODE, PROP_RETAINED, TOTAL_READS, RETAINED, AMBIG_READS, LQ_READS)
# number of samples in index
index <- "CAGATC"
lib <- "CLE3"
n_samples <- 29
# read in summary per barcode
l[["CLE3-7"]] <- read_table2("data/SEQ/CLE3_CAGATC_radtags.log",
            skip = 13, n_max = n_samples,
            col_names = c("BARCODE", "TOTAL_READS", "AMBIG_READS", "LQ_READS", "RETAINED")) %>%
  mutate(PROP_RETAINED = RETAINED/TOTAL_READS,
         INDEX = index,
         LIBRARY = lib) %>%
  select(LIBRARY, INDEX, BARCODE, PROP_RETAINED, TOTAL_READS, RETAINED, AMBIG_READS, LQ_READS)
# number of samples in index
index <- "TAGCTT"
lib <- "CLE3"
n_samples <- 29
# read in summary per barcode
l[["CLE3-10"]] <- read_table2("data/SEQ/CLE3_TAGCTT_radtags.log",
            skip = 13, n_max = n_samples,
            col_names = c("BARCODE", "TOTAL_READS", "AMBIG_READS", "LQ_READS", "RETAINED")) %>%
  mutate(PROP_RETAINED = RETAINED/TOTAL_READS,
         INDEX = index,
         LIBRARY = lib) %>%
  select(LIBRARY, INDEX, BARCODE, PROP_RETAINED, TOTAL_READS, RETAINED, AMBIG_READS, LQ_READS)
# create single data frame
radtagslog <- ldply(l, data.frame) %>%
  select(-`.id`, LQ_READS) %>%
  unite(LIB_IDX, LIBRARY, INDEX, sep = "_", remove = FALSE)
write_delim(radtagslog, "results/all.radtags.log")

Compare demultiplexed reads per library & index.

Plot distributions of total and proportion of retained reads.

radtagslog %>%
  select(LIB_IDX, LIBRARY, INDEX, BARCODE, PROP_RETAINED, TOTAL_READS) %>%
  gather(key = STAT, value = READS, 5:6) %>%
  ggplot(aes(x = READS)) +
  geom_histogram(color = "black", fill = "darkorange") +
  labs(x = "reads") +
  facet_grid(LIB_IDX ~ STAT, scales = "free") +
  theme_standard

Reference construction

Values chosen for MiSeq Reference:

Read mapping

Map reads using BWA (in dDocent pipeline)

Transfer copy of reference.fasta and associated files for K1 = 5 and K2 = 6 into each directory containing demultiplexed and quality trimmed sequences

Any renaming of files needs to happen before fastq-files are mapped. The population designation (before underscore) is used by FreeBayes to call SNPs so it is important that population designation make biological sense. All files should be named POP_PLATE-WELL_SAMPLENAMES.

Run dDocent from within each Library directory to map reads to reference.fasta.

QA/QC read mapping

Query mapping statistics

During the mapping stage, dDocent calls BWA to map reads from the individuals in the folder to the generated MiSeqReference and create a -RG.bam-file for each individual. The second column of a BAM (or SAM) file contains FLAGs with binary encoded information on mapping, pairedness etc. that can be used to compare the mapping efficiency of the generated MiSeq references.

Count number of reads and mapped reads using samtools idxstats <aln-RG.bam> which will retrieve and print stats in the bam-file. The output is TAB-delimited with each line consisting of reference sequence name, sequence length, # mapped reads and # unmapped (empty) reads. samtools can also be be used to query samtools flagstat file.bam which returns an output containing the number of reads for which each flag is true.

Run Flagstats

dDocent writes out a file called bamlist.list that contains all the bam files that were generated during read mapping in dDocent using BWA. Write script to gather flagstats from all bam-files.


# write script to gather flagstats
l <- list()

l[["CLE2"]] <- read_table2("data/SEQ/CLE-2/bamlist.list", col_names = "BAM") %>%
  mutate(PATH = "data/SEQ/CLE-2/",
         COMMAND = "samtools flagstat",
         OUT = ">> data/SEQ/CLE2.flagstats") %>%
  select( COMMAND, PATH, BAM, OUT) %>%
  unite(FILE, 2:3, sep = "")

l[["CLE3"]] <- read_table2("data/SEQ/CLE-3/bamlist.list", col_names = "BAM") %>%
  mutate(PATH = "data/SEQ/CLE-3/",
         COMMAND = "samtools flagstat",
         OUT = ">> data/SEQ/CLE3.flagstats") %>%
  select( COMMAND, PATH, BAM, OUT) %>%
  unite(FILE, 2:3, sep = "")

bam <- ldply(l, data.frame) %>%
  select(-`.id`)

write_delim(bam, "scr/flagstats.sh", delim = "\t", col_names = FALSE)

Run flagstats.

Run idxstats

Write script to gather idxstats from all bam-files.


# write script to gather idxstats
l <- list()

l[["CLE2"]] <- read_table2("data/SEQ/CLE-2/bamlist.list", col_names = "BAM") %>%
  mutate(PATH = "data/SEQ/CLE-2/",
         COMMAND = "samtools idxstats",
         OUT = ">> data/SEQ/CLE2.idxstats") %>%
  select( COMMAND, PATH, BAM, OUT) %>%
  unite(FILE, 2:3, sep = "")

l[["CLE3"]] <- read_table2("data/SEQ/CLE-3/bamlist.list", col_names = "BAM") %>%
  mutate(PATH = "data/SEQ/CLE-3/",
         COMMAND = "samtools idxstats",
         OUT = ">> data/SEQ/CLE3.idxstats") %>%
  select( COMMAND, PATH, BAM, OUT) %>%
  unite(FILE, 2:3, sep = "")

bam <- ldply(l, data.frame) %>%
  select(-`.id`)

write_delim(bam, "scr/idxstats.sh", delim = "\t", col_names = FALSE)

Run indxstats.

Format stats files into tidy data sets

Appending the file results in the information per individual being printed in a new set of row being appended to the file, i.e. there will be as many rows for a given locus as individuals were mapped. The file can be re-formatted and summary statistics calculated using dplyr and tidyr.

Format idxstats:

# create vectors of files to be imported, reference codes, K1 and K2, dataframe names
filenames <- list.files(path = "data/SEQ", pattern = "*.idxstats")
names <- substr(filenames, 1, 8)
lib <- substr(filenames, 1, 4)
# import data
for (i in names){
  filepath <- file.path("data/SEQ", paste(i, 'stats', sep =""))
  assign(i, read.table(filepath, sep = "", header = FALSE,
                     col.names = c("Locus", "Length", "Reads_Mapped", 'blank')) %>%
           select(1:3))
  }
# # make sure to delete old list if rerunning the code
# rm(dflist_idx)
# rm(MapStats.idx)
# Create list of one dataframe per idxstats file and group by locus
dflist_idx <- lapply(ls(pattern = "*.idx"), get)
for (df in 1:length(dflist_idx)){
  x <- dflist_idx[[df]]
  x[['Locus']] <- as.character(x[['Locus']])
  x = x %>% group_by(Locus)
  dflist_idx[[df]] <- x
}
# Create new dataframes with summary stats per library and bind into final output/dataframe
MapStats.idx <- data.frame()
for (df in 1:length(dflist_idx)){
    
  x = summarize(dflist_idx[[df]],
                Length = mean(Length),
                Mean_Mapped = mean(Reads_Mapped),
                Sum_Mapped = sum(Reads_Mapped),
                Min_Mapped = min(Reads_Mapped),
                Max_Mapped = max(Reads_Mapped),
                SD_Mapped = sd(Reads_Mapped))
  x[x == 0] <- NA
  temp <- summarize(x, Mean_Mapped_Non0 = mean(Mean_Mapped, na.rm = TRUE)) %>%
    mutate(Lib = lib[df],
           Not_Mapped = nrow(filter(x, is.na(Sum_Mapped))),
           N_Loci_Ref = nrow(x)) %>%
    select(Lib, N_Loci_Ref, Not_Mapped, Mean_Mapped_Non0)
  MapStats.idx <- bind_rows(MapStats.idx, temp)
}
MapStats.idx <- MapStats.idx %>%
  mutate(PROP_EMPTY = round(Not_Mapped/N_Loci_Ref*100, digits = 2),
         CONTIGS_MAPPED = N_Loci_Ref - Not_Mapped)
write.table(MapStats.idx, "results/MapStats.idx", quote = FALSE)
# remove large (duplicate) files
rm(CLE2.idx)
rm(CLE3.idx)
MapStats.idx

Format flagstats

# Files to be imported
filenames <- list.files(path='data/SEQ', pattern = '*.flagstats')
# create vectors of files to be imported
names <- substr(filenames, 1, 9)
lib <- substr(filenames, 1, 4)
# import data
for (i in names){
  filepath <- file.path('data/SEQ', paste(i, 'stats', sep =""))
  assign(i, read.csv(filepath, sep = "+", header = FALSE,
                     col.names = c("N_Reads", "CAT"),
                     stringsAsFactors = FALSE) %>%
           select(1:2))
}
# Create list of one dataframe per flagstats file and create tidy data set
# should be 3 elements/libraries
rm(dflist_flag)
object 'dflist_flag' not found
dflist_flag <- lapply(ls(pattern = "*flag"), get)
# Change N_Reads to numeric
for (df in 1:length(dflist_flag)){
  x <- dflist_flag[[df]]
  x[['N_Reads']] <- as.numeric(x[['N_Reads']])
  dflist_flag[[df]] <- x
}
NAs introduced by coercionNAs introduced by coercion
for (df in 1:length(dflist_flag)){
  x <- dflist_flag[[df]]
  
  n <- nrow(x)/14
  x <- x %>%
    filter(grepl("0 mapped|properly paired|mapQ>=5", CAT)) %>%
    mutate(MAPSTAT = ifelse(grepl("mapQ>=5", CAT), "Mismatch",
                   ifelse(grepl("properly", CAT), "Prop_Paired", "Mapped"))) %>%
    mutate(Ind = c(rep(1:n, each = 3))) %>% 
    # not sure if extra individual in there somehow
    select(4, 3, 1) %>%
    spread(MAPSTAT, N_Reads)
  dflist_flag[[df]] <- x
}
# Create new dataframes with summary stats and add to main final data frame
MapStats.flag <- data.frame()
for (df in 1:length(dflist_flag)){
  x = summarize(dflist_flag[[df]], Sum_Mapped = sum(Mapped),
                             Reads_Mapped = mean(Mapped),
                             Sum_Paired = sum(Prop_Paired),
                             Mean_Paired = mean(Prop_Paired),
                             Sum_Mismatch = sum(Mismatch),
                             Mean_Mismatch = mean(Mismatch)) %>%
  mutate(Lib = lib[df]) %>%
  select(7, 1:6)
  MapStats.flag <- bind_rows(MapStats.flag, x)
}
# write to file
write.table(MapStats.flag, "results/MapStats.flag", quote = FALSE)
MapStats.flag
# combine files
mapstats <- left_join(MapStats.idx, MapStats.flag) %>%
  mutate(PROP_MISMATCH = Sum_Mismatch/Sum_Mapped)
Joining, by = "Lib"
# write summary stats file
write.table(mapstats, file = "results/BWA_mapping.stats", quote = FALSE, sep = " ")

Evaluate & compare mapping results

Compare number of reference contigs for which no reads mapped to per library.

# plot no of loci vs "empty" loci
p1 <- ggplot(mapstats, aes(x = Lib, y = PROP_EMPTY)) +
  geom_bar(stat = "identity", color = "black", fill = "darkorange") +
  scale_y_continuous(limits = c(0, 6)) +
  labs(x = "", y = "% contigs w/no reads mapped") +
  theme_standard
p2 <- ggplot(mapstats, aes(x = Lib, y = Reads_Mapped)) +
  geom_bar(stat = "identity", color = "black", fill = "darkorange") +
  labs(x = "library", y = "mean reads mapped per indv") +
  theme_standard
p3 <- ggplot(mapstats, aes(x = Lib, y = PROP_MISMATCH)) +
  geom_bar(stat = "identity", color = "black", fill = "darkorange") +
  labs(x = "",y = "% reads not mapped as pair") +
  theme_standard
multiplot(p1, p2, p3, cols = 3)

SNP calling

Transfer copy of reference.fasta and associated files for K1 = 4 and K2 = 1 into SNP calling directory.

Create symlinks from all fq, bam and bam.bai-files for each separately mapped library in SNP_Calling folder.

Execute dDocent from within SNP_Calling-folder to call variants across all individuals (all libraries).

File TotalrawSNPs.vcf contains all raw SNP/INDEL calls. Do not need to keep links of fq.gz-, bam-, .bam.bai-files after SNPs have been called. Copy TotalRaSNPs.vcf to VCF_Filtering for SNP filtering.

SNP filtering

dDocent uses FreeBayes to call SNPs and write a VCF-file TotalrawSNPs.vcf. This data set was filtered to remove low quality and artefactual SNP sites, paralogs and low quality individuals based on levels of missing data, minimum/maximum read depth, genotype call rate, and minor allele frequencies. Contigs may contain more than one SNP; the script rad_haplotyper.pl was used to create haplotypes for each locus.

Raw data

Individuals/Populations sampled

Generate a list of all individuals included in the SNP data set called by FreeBayes in the dDocent pipeline.


vcfsamplenames data/VCF/temp/TotalRawSNPs.vcf > data/VCF/raw.ind

Use Ind_raw file to write text files of individuals in each library and in regional groupings.

Compare Individual & SNP stats

Use VCFtools to create stats files for depth, missing data, heterozygosity and site quality for the raw data.


vcftools --vcf data/VCF/temp/TotalRawSNPs.vcf --out data/VCF/CLE_raw --depth
vcftools --vcf data/VCF/temp/TotalRawSNPs.vcf --out data/VCF/CLE_raw --site-mean-depth
vcftools --vcf data/VCF/temp/TotalRawSNPs.vcf --out data/VCF/CLE_raw --site-quality
vcftools --vcf data/VCF/temp/TotalRawSNPs.vcf --out data/VCF/CLE_raw --missing-indv
vcftools --vcf data/VCF/temp/TotalRawSNPs.vcf --out data/VCF/CLE_raw --missing-site
vcftools --vcf data/VCF/temp/TotalRawSNPs.vcf --out data/VCF/CLE_raw --het
vcftools --vcf data/VCF/temp/TotalRawSNPs.vcf --out data/VCF/CLE_raw --singletons

Compare raw stats.

# load stats files ----
ind_stats_raw <- read.ind.stats(dir = "data/VCF", vcf = "CLE_raw") %>%
  separate(INDV, into = c("SP", "LIB", "SAMPLE_ID"), sep = "_", remove = FALSE)
loc_stats_raw <- read.loc.stats(dir = "data/VCF/", vcf = "CLE_raw")
# plot missing data per indv ----
p1 <- ggplot(ind_stats_raw, aes(x = MISS_CLE_raw)) +
  geom_histogram(binwidth = .01, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MISS_CLE_raw, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0.5),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "missing data per indv") +
  theme_standard
# plot read depth per indv ----
p2 <- ggplot(ind_stats_raw, aes(x = MEAN_DEPTH_CLE_raw)) +
  geom_histogram(binwidth = 10, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE_raw, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean read depth per indv") +
  theme_standard
# plot depth vs missing ----
p3 <- ggplot(ind_stats_raw, aes(x = MEAN_DEPTH_CLE_raw, y = MISS_CLE_raw)) +
  geom_point() +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE_raw, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(MISS_CLE_raw, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 0.5),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean depth per indv", y = "% missing data") +
  theme_standard
# plot Fis per indv ----
p4 <- ggplot(ind_stats_raw, aes(x = Fis_CLE_raw)) +
  geom_histogram(binwidth = .01, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(Fis_CLE_raw, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "Fis per indv") +
  theme_standard
# plot Fis vs missing data per indv ----
p5 <- ggplot(ind_stats_raw, aes(x = Fis_CLE_raw, y = MISS_CLE_raw)) +
  geom_point() +
  geom_vline(aes(xintercept = mean(Fis_CLE_raw, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0),
                 color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(MISS_CLE_raw, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 0.5),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "Fis per indv", y = "% missing data") +
  theme_standard
# plot Fis vs mean depth per indv ----
p6 <- ggplot(ind_stats_raw, aes(x = Fis_CLE_raw, y = MEAN_DEPTH_CLE_raw)) +
  geom_point() +
  geom_vline(aes(xintercept = mean(Fis_CLE_raw, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0),
                 color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(MEAN_DEPTH_CLE_raw, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "Fis per indv", y = "mean depth per indv") +
  theme_standard
# plot distribution missing data per locus ----
p7 <- ggplot(loc_stats_raw, aes(x = MISS_CLE_raw)) +
  geom_histogram(binwidth = 0.01, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MISS_CLE_raw, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0.1),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "% missing data per locus") +
  theme_standard
# plot distribution mean read depth ----
p8 <- ggplot(loc_stats_raw, aes(x = MEAN_DEPTH_CLE_raw)) +
  geom_histogram(binwidth = 20, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE_raw, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean read depth per locus") +
  theme_standard
# plot read depth vs missing data ----
p9 <- ggplot(loc_stats_raw, aes(x = MEAN_DEPTH_CLE_raw, y = MISS_CLE_raw)) +
  geom_point() +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE_raw, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(MISS_CLE_raw, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 0.1),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean depth per locus", y = "% missing data") +
  theme_standard
# plot depth vs SNP quality ----
site_qual <- read.table("data/VCF/CLE_raw.lqual", 
                        header = TRUE, stringsAsFactors = FALSE) %>%
  mutate(PROB = 10^(-QUAL/10))
temp <- data.frame(loc_stats_raw$MEAN_DEPTH_CLE_raw, site_qual$QUAL) %>%
  rename(depth = loc_stats_raw.MEAN_DEPTH_CLE_raw, qual = site_qual.QUAL)
p10 <- ggplot(temp, aes(x = depth, y = qual)) +
  geom_point(size = 1) +
  geom_vline(aes(xintercept = mean(depth, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(qual, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean depth per locus", y = "SNP quality") +
  theme_standard
# plot number of SNPs per contig vs. mean depth ----
temp <- loc_stats_raw %>%
  count(CHR)
p11 <- left_join(temp, loc_stats_raw) %>%
  ggplot() +
  geom_point(aes(x = n, y = MEAN_DEPTH_CLE_raw)) +
  labs(x = "number of SNPs per contig", y = "mean depth") +
  theme_standard
# plot no of SNPs per locus ----
p12 <- loc_stats_raw %>%
  count(CHR) %>%
  ggplot(aes(x = n)) +
  geom_histogram(binwidth = 1, color = "black", fill = "darkorange") + 
  labs(x = "number of SNPs per locus") +
  theme_standard
mraw <- multiplot(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, cols=2)

Choose threshold values for quality score, coverage, missing data, minor alleles and mapping/variant calling artifacts

Filter 0: Remove LQ individuals

Remove (known) low quality individuals from data set:

Identify low quality individuals to remove from the data set; defined as individuals with a mean coverage of three or less reads across all loci and more than 85% missing data:


LQindv <- ind_stats_raw %>%
  filter(MEAN_DEPTH_CLE_raw < 5 | MISS_CLE_raw >= 0.75) %>%
  select(INDV)

# View(LQindv)

write_delim(LQindv, "data/VCF/LQ_raw.ind", delim = "\t")

Remove low quality individuals and decompose indels.

Compare stats:

# load stats files ----
ind_stats_F0 <- read.ind.stats(dir = "data/VCF", vcf = "CLE.F0") %>%
  separate(INDV, into = c("SP", "LIB", "SAMPLE_ID"), sep = "_", remove = FALSE, extra = "merge")
Joining, by = "INDV"
Joining, by = "INDV"
loc_stats_F0 <- read.loc.stats(dir = "data/VCF/", vcf = "CLE.F0")
Joining, by = c("CHR", "POS")

Import singletons and genotype depth file to create list of loci to exclude.


# number of individuals
n <- nrow(ind_stats_F0)+2

# read singletons file
singletons <- read_table2("data/VCF/CLE.F0.singletons") %>%
  mutate(VARIANT = ifelse(ALLELE %in% c("A", "T", "C", "G"), "SNP", "INDEL"))

chrom <- unique(singletons$CHROM)

# read genotype depth file and join with singletons
gdepth <- read_table2("data/VCF/CLE.F0.gdepth") %>%
  filter(CHROM %in% chrom) %>%
  gather(key = INDV, value = DEPTH, 3:n)

singletons <- left_join(singletons, gdepth)
  

Data set contains nrow(singletons) singletons.

# filter doubletons
doubletons <- singletons %>%
  filter(`SINGLETON/DOUBLETON` == "D")

Of those nrow(doubletons) are called as a homozygote in one individual.

# number of contigs in data set
contigs_total <- loc_stats_F0 %>%
  distinct(CHROM)
contigs_singletons <- singletons %>%
  distinct(CHROM)
contigs <- singletons %>%
  count(CHROM)
ggplot(contigs, aes(x = n)) +
  geom_histogram(binwidth = 1, color = "black", fill = "darkorange") + 
  geom_vline(aes(xintercept = mean(n, na.rm = TRUE)),
             color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "number of singletons per locus") +
  theme_standard

The data set contains nrow(contigs_total) contigs, nrow(contigs_singletons) (2round( (nrow(contigs_singletons)/nrow(contigs_total)*100), digits = 2)%) contain singletons.

contigs <- singletons %>%
  group_by(`SINGLETON/DOUBLETON`) %>%
  count(CHROM)
ggplot(contigs, aes(x = n)) +
  geom_histogram(binwidth = 1, color = "black", fill = "darkorange") + 
  facet_grid(. ~ `SINGLETON/DOUBLETON`) +
  labs(x = "number of doubletons/singeltons per locus") +
  theme_standard

Target is 20 reads per locus; identify number of singletons/doubletons with depth < 10 reads.

count(singletons, DEPTH <= 5)

Distriubtion of genotype depth per singleton/doubleton.

ggplot(singletons, aes(x = DEPTH)) +
  geom_histogram(binwidth = 20, color = "black", fill = "darkorange") +
  facet_grid(. ~ `SINGLETON/DOUBLETON`, scales = "free") +
  labs(x = "read depth") +
  scale_y_sqrt() +
  theme_standard

Quantiles distribution of read depth for SNP loci called in only one individual.

quantile(singletons$DEPTH, probs = c(.05, .25, .5, .75, .95, .99))
    5%    25%    50%    75%    95%    99% 
  2.00   6.00  11.00  59.00 191.00 304.29 

Distribution of doubletons (homzygous genotype for that individual) and singletons (heterozygote genotype).

Ind <- singletons %>%
  group_by(`SINGLETON/DOUBLETON`) %>%
  count(INDV)
Ind <- left_join(Ind, ind_stats_F0)
ggplot(Ind, aes(x = n)) +
  geom_histogram(binwidth = 50, color = "black", fill = "darkorange") + 
  facet_grid(. ~ `SINGLETON/DOUBLETON`) +
  labs(x = "number of singletons per indv") +
  theme_standard

Quantiles distribution number of singletons called in one individual.

quantile(Ind$n, probs = c(.01, .05, .25, .5, .75, .99))
   1%    5%   25%   50%   75%   99% 
  1.0   3.5  10.0  29.0  92.5 504.1 

Compare the number of SNPs vs complex variants (INDELs).

count(singletons, VARIANT)

Filter 1: Confidence in SNP call

The QUAL column of a VCF file is a phred based score indicating the probability that the variant shown in the ALT column is wrong. Given the Phred quality score (Q), and the probability that a base is incorrectly called (P), Q = -10(Log10P).

A quality score of 20 indicates, a 1 in 100 chance that the SNP site has been called incorrectly (i.e. 99% probability that correct call).

Filter loci with quality score < 20. Code genotypes with genotype call quality < 20 or genotype depth < 5 as missing.


# filter LQ SNP calls
vcftools --vcf data/VCF/temp/CLE.F0.recode.vcf --out data/VCF/temp/CLE.F1 --minQ 20 --minGQ 20 --minDP 5 --recode --recode-INFO-all

vcftools --vcf data/VCF/temp/CLE.F1.recode.vcf --out data/VCF/F1 --keep data/VCF/duplicate.ind --012
vcftools --vcf data/VCF/temp/CLE.F1.recode.vcf --out data/VCF/F1 --keep data/VCF/duplicate.ind --geno-depth

# query stats
vcftools --vcf data/VCF/temp/CLE.F1.recode.vcf --out data/VCF/CLE.F1 --depth
vcftools --vcf data/VCF/temp/CLE.F1.recode.vcf --out data/VCF/CLE.F1 --site-mean-depth
vcftools --vcf data/VCF/temp/CLE.F1.recode.vcf --out data/VCF/CLE.F1 --missing-indv
vcftools --vcf data/VCF/temp/CLE.F1.recode.vcf --out data/VCF/CLE.F1 --missing-site
vcftools --vcf data/VCF/temp/CLE.F1.recode.vcf --out data/VCF/CLE.F1 --het
vcftools --vcf data/VCF/temp/CLE.F1.recode.vcf --out data/VCF/CLE.F1 --geno-depth
vcftools --vcf data/VCF/temp/CLE.F1.recode.vcf --out data/VCF/CLE.F1 --singletons
vcftools --vcf data/VCF/temp/CLE.F1.recode.vcf --out data/VCF/CLE.F1 --indv-freq-burden
vcftools --vcf data/VCF/temp/CLE.F1.recode.vcf --out data/VCF/CLE.F1 --freq 

Compare stats post-filtering.

# load stats files ----
ind_stats_F1 <- read.ind.stats(dir = "data/VCF", vcf = "CLE.F1") %>%
  separate(INDV, into = c("SP", "LIB", "SAMPLE_ID"), sep = "_", remove = FALSE)
loc_stats_F1 <- read.loc.stats(dir = "data/VCF/", vcf = "CLE.F1")

Data set contains 246 individuals and 98061 SNP sites.

Removing low confidence SNP loci and genotype calls results in a reduction in the number of the (maximum) SNPs per locus.

p1 <- loc_stats_raw %>%
  count(CHR) %>%
  ggplot(aes(x = n)) +
  geom_histogram(binwidth = 1, color = "black", fill = "darkorange") + 
  geom_vline(aes(xintercept = mean(n, na.rm = TRUE)),
             color = "darkblue", linetype = "dashed", size = 1) +
  scale_x_continuous(limits = c(0, 100)) +
  labs(x = "number of SNPs per locus") +
  theme_standard
p2 <- loc_stats_F1 %>%
  count(CHR) %>%
  ggplot(aes(x = n)) +
  geom_histogram(binwidth = 1, color = "black", fill = "darkorange") + 
  geom_vline(aes(xintercept = mean(n, na.rm = TRUE)),
             color = "darkblue", linetype = "dashed", size = 1) +
  scale_x_continuous(limits = c(0, 100)) +
  labs(x = "number of SNPs per locus") +
  theme_standard
m1a <- multiplot(p1, p2, cols=1)

Coding genotypes with low read depth (< 3) as missing, results in an overall increase in missing data per locus and a shift in more individuals with more missing data - this is because individuals (and loci) with coverage issues are now characterized by higher missing data.

iraw <- read.table("data/VCF/CLE_raw.imiss",
                    header = TRUE, stringsAsFactors = FALSE) %>%
  select(INDV, F_MISS) %>%
  rename(raw = F_MISS)
imiss <- read.table("data/VCF/CLE.F1.imiss",
                    header = TRUE, stringsAsFactors = FALSE) %>%
  select(INDV, F_MISS) %>%
  rename(F1 = F_MISS)
imiss <- left_join(imiss, iraw)
p1 <- ggplot(imiss, aes(x = raw, y = F1)) +
  geom_point(shape = 1) +
  geom_abline(slope = 1, color = "darkblue", linetype = "dashed", size = 1) +
  scale_x_continuous(limits = c(0, 1)) +
  scale_y_continuous(limits = c(0, 1)) +
  labs(x = "indv missing data raw", y = "indv missing data F1") +
  theme_standard
lraw <- read.table("data/VCF/CLE_raw.lmiss",
                    header = TRUE, stringsAsFactors = FALSE) %>%
  select(CHR, POS, F_MISS) %>%
  rename(raw = F_MISS)
lmiss <- read.table("data/VCF/CLE.F1.lmiss",
                    header = TRUE, stringsAsFactors = FALSE) %>%
  select(CHR, POS, F_MISS) %>%
  rename(F1 = F_MISS)
lmiss <- left_join(lmiss, lraw)
p2 <- ggplot(lmiss, aes(x = raw, y = F1)) +
  geom_point(shape = 1) +
  geom_abline(slope = 1, color = "darkblue", linetype = "dashed", size = 1) +
  scale_x_continuous(limits = c(0, 1)) +
  scale_y_continuous(limits = c(0, 1)) +
  labs(x = "loci missing data raw", y = "loci missing data F1") +
  theme_standard
m1b <- multiplot(p1, p2, cols=2)

Compare depth individuals after removing LQ SNP loci and genotypes.

iraw <- read.table("data/VCF/CLE_raw.idepth",
                    header = TRUE, stringsAsFactors = FALSE) %>%
  select(INDV, MEAN_DEPTH) %>%
  rename(raw = MEAN_DEPTH)
idepth <- read.table("data/VCF/CLE.F1.idepth",
                    header = TRUE, stringsAsFactors = FALSE) %>%
  select(INDV, MEAN_DEPTH) %>%
  rename(F1 = MEAN_DEPTH)
idepth <- left_join(iraw, idepth)
ggplot(idepth, aes(x = raw, y = F1)) +
  geom_point(shape = 1) +
  geom_abline(slope = 1, color = "darkblue", linetype = "dashed", size = 1) +
  scale_x_continuous(limits = c(0, 150)) +
  scale_y_continuous(limits = c(0, 150)) +
  labs(x = "mean depth ind raw", y = "mean depth ind F1") +
  theme_standard

Filter 2: Genotype call rate and allowed missing data per indv

Remove loci with genotype call rate < 50% and mean depth < 20 reads across all individuals.


vcftools --vcf data/VCF/temp/CLE.F1.recode.vcf --out data/VCF/temp/CLE.F2a --max-missing 0.5 --min-meanDP 15 --recode --recode-INFO-all

vcftools --vcf data/VCF/temp/CLE.F2a.recode.vcf --out data/VCF/F2a --keep data/VCF/duplicate.ind --012
vcftools --vcf data/VCF/temp/CLE.F2a.recode.vcf --out data/VCF/F2a --keep data/VCF/duplicate.ind --geno-depth

# library CLE-4
vcftools --vcf data/VCF/temp/CLE.F2a.recode.vcf --out data/VCF/temp/CLE2 --keep data/VCF/CLE2.ind  --recode --recode-INFO-all

vcftools --vcf data/VCF/temp/CLE2.recode.vcf --out data/VCF/CLE2 --missing-site

# library CLE-5
vcftools --vcf data/VCF/temp/CLE.F2a.recode.vcf --out data/VCF/temp/CLE3 --keep data/VCF/CLE3.ind  --recode --recode-INFO-all

vcftools --vcf data/VCF/temp/CLE3.recode.vcf --out data/VCF/CLE3 --missing-site

# Aransas Bay
vcftools --vcf data/VCF/temp/CLE.F2a.recode.vcf --out data/VCF/temp/ARA --keep data/VCF/ARA.ind  --recode --recode-INFO-all
vcftools --vcf data/VCF/temp/ARA.recode.vcf --out data/VCF/ARA --missing-site

# Corpus Christi Bay
vcftools --vcf data/VCF/temp/CLE.F2a.recode.vcf --out data/VCF/temp/CC --keep data/VCF/CC.ind  --recode --recode-INFO-all
vcftools --vcf data/VCF/temp/CC.recode.vcf --out data/VCF/CC --missing-site

# Galveston Bay
vcftools --vcf data/VCF/temp/CLE.F2a.recode.vcf --out data/VCF/temp/GAL --keep data/VCF/GAL.ind  --recode --recode-INFO-all
vcftools --vcf data/VCF/temp/GAL.recode.vcf --out data/VCF/GAL --missing-site

# Matagorda Bay
vcftools --vcf data/VCF/temp/CLE.F2a.recode.vcf --out data/VCF/temp/MAT --keep data/VCF/MAT.ind  --recode --recode-INFO-all
vcftools --vcf data/VCF/temp/MAT.recode.vcf --out data/VCF/MAT --missing-site

# San Antionio
vcftools --vcf data/VCF/temp/CLE.F2a.recode.vcf --out data/VCF/temp/SA --keep data/VCF/SA.ind  --recode --recode-INFO-all
vcftools --vcf data/VCF/temp/SA.recode.vcf --out data/VCF/SA --missing-site

# Sabine Lake
vcftools --vcf data/VCF/temp/CLE.F2a.recode.vcf --out data/VCF/temp/SL --keep data/VCF/SL.ind  --recode --recode-INFO-all
vcftools --vcf data/VCF/temp/SL.recode.vcf --out data/VCF/SL --missing-site

Compare distribution of missing data per locus per library.

# create empty list
loc_missing <- list()
# import missing data per locus
loc_missing[[1]] <- read.table("data/VCF/CLE2.lmiss", 
                        header = TRUE, stringsAsFactors = FALSE) %>%
  select(CHR, POS, F_MISS) %>%
  mutate(LIB = "CLE2")
loc_missing[[2]] <- read.table("data/VCF/CLE3.lmiss", 
                        header = TRUE, stringsAsFactors = FALSE) %>%
  select(CHR, POS, F_MISS) %>%
  mutate(LIB = "CLE3")
# create data frame with all information
loc_missing <- ldply(loc_missing, data.frame)
ggplot(loc_missing, aes(x = F_MISS)) +
  geom_histogram(binwidth = .1, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = 0.5),
             color = "darkred", linetype = "dashed", size = 1) +
  labs(x = "missing data per locus") +
  facet_grid(LIB ~ .) +
  theme_standard

Flag loci that were not called in > 25% of individuals in a given library.

# identify loci with high missing data in each library
SNPs <- filter(loc_missing, F_MISS > 0.5) %>%
  arrange(CHR, POS)
count(SNPs, LIB)
LQloci_lib <- SNPs %>%
  select(CHR, POS) %>%
  unique()
A total of

loci were called in less than 50% of individuals in one or more libraries. Loci being inconsistently called between libraries can result in library effects.

Compare distribution of missing data per locus per sample location.

Flag loci that were not called in > 75% of individuals at a given sample location.

# identify loci with high missing data in each library
SNPs <- filter(loc_missing, F_MISS > 0.25) %>%
  arrange(CHR, POS)
count(SNPs, POP)
LQloci_pop <- SNPs %>%
  select(CHR, POS) %>%
  unique()
LQloci <- bind_rows(LQloci_lib, LQloci_pop) %>%
  unique()
# Write contig/position to file
write.table(LQloci, file = "data/VCF/LQ_F2b.loci", 
            col.names= FALSE, row.names = FALSE, quote = FALSE)

Remove loci that were not consistently called across all libraries and sample locations.


vcftools --vcf data/VCF/temp/CLE.F2a.recode.vcf --out data/VCF/temp/CLE.F2b --exclude-positions data/VCF/LQ_F2b.loci --recode --recode-INFO-all

vcftools --vcf data/VCF/temp/CLE.F2b.recode.vcf --out data/VCF/F2b --keep data/VCF/duplicate.ind --012
vcftools --vcf data/VCF/temp/CLE.F2b.recode.vcf --out data/VCF/F2b --keep data/VCF/duplicate.ind --geno-depth

vcftools --vcf data/VCF/temp/CLE.F2b.recode.vcf --out data/VCF/CLE.F2b --missing-indv

Identify individuals with > 50% missing data

# determine cutoff
imiss <- read.table("data/VCF/CLE.F2b.imiss", 
           header = TRUE, stringsAsFactors = FALSE) 
ggplot(imiss, aes(x = F_MISS)) +
  geom_histogram(binwidth = 0.01, color = "black", fill = "darkorange") +
  geom_vline(xintercept = 0.25, color = "darkblue", linetype = "dashed", size = 1) +
  theme_standard

imiss <- imiss %>%
  filter(F_MISS > 0.25) %>%
  select(INDV)
View(imiss)
write.table(imiss, "data/VCF/F2b_LQ.indv",
            col.names = TRUE, row.names = FALSE, quote = FALSE)

Remove flagged individuals.


vcftools --vcf data/VCF/temp/CLE.F2b.recode.vcf --out data/VCF/temp/CLE.F2 --remove data/VCF/F2b_LQ.indv --recode --recode-INFO-all

vcftools --vcf data/VCF/temp/CLE.F2.recode.vcf --out data/VCF/F2 --keep data/VCF/duplicate.ind --012
vcftools --vcf data/VCF/temp/CLE.F2.recode.vcf --out data/VCF/F2 --keep data/VCF/duplicate.ind --geno-depth

vcftools --vcf data/VCF/temp/CLE.F2.recode.vcf --out data/VCF/CLE.F2 --depth
vcftools --vcf data/VCF/temp/CLE.F2.recode.vcf --out data/VCF/CLE.F2 --site-mean-depth
vcftools --vcf data/VCF/temp/CLE.F2.recode.vcf --out data/VCF/CLE.F2 --missing-indv
vcftools --vcf data/VCF/temp/CLE.F2.recode.vcf --out data/VCF/CLE.F2 --missing-site
vcftools --vcf data/VCF/temp/CLE.F2.recode.vcf --out data/VCF/CLE.F2 --het
vcftools --vcf data/VCF/temp/CLE.F2.recode.vcf --out data/VCF/CLE.F2 --geno-depth

Analyze stats post-filtering:

# load stats files ----
ind_stats_F2 <- read.ind.stats(dir = "data/VCF", vcf = "CLE.F2") %>%
  separate(INDV, into = c("SP", "LIB", "SAMPLE_ID"), sep = "_", remove = FALSE)
loc_stats_F2 <- read.loc.stats(dir = "data/VCF/", vcf = "CLE.F2")

Compare change in missing data of loci and individuals.

Filter 3: Filter loci and individuals based on depth, variance in depth and genotype call rate

Determine mean depth and variance per locus per library.


# library CLE-4
vcftools --vcf data/VCF/temp/CLE.F2.recode.vcf --out data/VCF/temp/CLE2 --keep data/VCF/CLE2.ind  --recode --recode-INFO-all

vcftools --vcf data/VCF/temp/CLE2.recode.vcf --out data/VCF/CLE2 --site-mean-depth

# library CLE-5
vcftools --vcf data/VCF/temp/CLE.F2.recode.vcf --out data/VCF/temp/CLE3 --keep data/VCF/CLE3.ind  --recode --recode-INFO-all

vcftools --vcf data/VCF/temp/CLE3.recode.vcf --out data/VCF/CLE3 --site-mean-depth

Compare distribution of depth coverage per locus per library. Identify loci that do not have consistent coverage between libraries (can lead to library effects), and/or across individuals.

Identify loci with large variance in mean depth across libraries and/or individuals by calculating the coefficient of variance (STD/MEAN).

# calculate mean depth weighted by library
depth_comp <- loc_depth %>%
  group_by(LIB) %>%
  distinct(CHROM, .keep_all = TRUE) %>%
  select(-POS) %>%
  ungroup() %>%
  group_by(CHROM) %>%
  summarise(MEAN = mean(MEAN_DEPTH),
            STD = sd(MEAN_DEPTH))
# mean depth across all individuals
temp <- read.table("data/VCF/CLE.F2.ldepth.mean", 
                   header = TRUE, stringsAsFactors = FALSE) %>%
  distinct(CHROM, .keep_all = TRUE) %>%
  select(-POS) %>%
  mutate(STD_DEPTH = sqrt(VAR_DEPTH))
# calculate coefficient of variation
depth_comp <- left_join(depth_comp, temp) %>%
  mutate(COEFF_VAR_LIB = STD/MEAN*100,
         COEFF_VAR_IND = STD_DEPTH/MEAN_DEPTH*100)

Compare mean across all individual to mean weighted by library and coefficient of variance of read depth across individuals and between libraries:

p1 <- ggplot(depth_comp, aes(x = MEAN_DEPTH, y = MEAN)) +
  geom_point(shape = 1) +
  labs(x = "mean depth per locus", y = "mean weighted by lib") +
  geom_abline(slope = 1, linetype = "dashed", color = "darkred", size = 1) +
  theme_standard
p2 <- ggplot(depth_comp, aes(x = COEFF_VAR_IND)) +
  geom_histogram(binwidth = 10, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = quantile(COEFF_VAR_IND, .99)),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "coeff var depth across all indv") +
  theme_standard
p3 <- ggplot(depth_comp, aes(x = COEFF_VAR_IND, y = COEFF_VAR_LIB)) +
  geom_point(shape = 1) +
  scale_x_continuous(limits = c(0, 200)) +
  scale_y_continuous(limits = c(0, 100)) +
  labs(x = "coeff var depth across all indv", y = "coeff var mean depth betw lib") +
  theme_standard
p4 <- ggplot(depth_comp, aes(x = COEFF_VAR_LIB)) +
  geom_histogram(binwidth = 10, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = quantile(COEFF_VAR_LIB, .99)),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "coeff var depth between lib") +
  theme_standard
m3a <- multiplot(p1, p2, p3, p4, cols=2)

If loci have consistent coverage across loci the mean read depth per locus across all individuals and weighted by library should fall on the red diagonal.

Flag loci that have high variation in mean coverage between individuals and between libraries.

# identify loci high variation in coverage
contigs_var <- depth_comp %>%
  filter(COEFF_VAR_LIB > 90 | COEFF_VAR_IND > 120)
SNPs_var <- filter(loc_depth, CHROM %in% contigs_var$CHROM) %>%
  distinct(CHROM, POS)
# Write contig/position to text file, use file with vcftools to remove positions from dataset 
write.table(SNPs_var, file = "data/VCF/LQ_F3.loci", 
            col.names= FALSE, row.names = FALSE, quote = FALSE)

Remove loci flagged for high variance in depth across all individuals and between libraries (removes library effects), filter loci with mean depth < 20 and genotype call rate < 75%


vcftools --vcf data/VCF/temp/CLE.F2.recode.vcf --out data/VCF/temp/CLE.F3a --exclude-positions data/VCF/LQ_F3.loci --min-meanDP 15 --max-missing 0.75 --recode --recode-INFO-all

vcftools --vcf data/VCF/temp/CLE.F3a.recode.vcf --out data/VCF/F3a --keep data/VCF/duplicate.ind --012
vcftools --vcf data/VCF/temp/CLE.F3a.recode.vcf --out data/VCF/F3a --keep data/VCF/duplicate.ind --geno-depth

vcftools --vcf data/VCF/temp/CLE.F3a.recode.vcf --out data/VCF/CLE.F3a --depth
vcftools --vcf data/VCF/temp/CLE.F3a.recode.vcf --out data/VCF/CLE.F3a --geno-depth

Compare mean depth and number of sites called per individual.

Use genotype depth file to identify individuals with high variance in read depth across loci.


# number of individuals
n <- nrow(ind_stats_F2)+1

# read genotype depth & code values < 5 as missing
gdepth <- read_table2("data/VCF/CLE.F3a.gdepth") %>%
  select(-POS) %>%
  distinct(CHROM, .keep_all = TRUE) %>%
  gather(key = INDV, value = DEPTH, 3:n) %>%
  mutate(DEPTH = as.numeric(gsub(-1, 0, DEPTH)),
         DEPTH = as.numeric(gsub("\\<1\\>", 0, DEPTH)),
         DEPTH = as.numeric(gsub("\\<2\\>", 0, DEPTH)),
         DEPTH = as.numeric(gsub("\\<3\\>", 0, DEPTH)),
         DEPTH = as.numeric(gsub("\\<4\\>", 0, DEPTH)))

# calculate summary statistics
idepth <- gdepth %>%
  group_by(INDV) %>%
  summarize(TOTAL = sum(DEPTH),
            MAX = max(DEPTH),
            MEAN_NON0 = mean(DEPTH[DEPTH > 0]),
            MEAN = mean(DEPTH),
            MEDIAN = median(DEPTH),
            VAR = var(DEPTH),
            STD = sd(DEPTH)) %>%
  mutate(COEFF_VAR = STD/MEAN,
         RATIO_MEAN_MEDIAN = MEAN/MEDIAN)

idepth <- left_join(idepth, ind_stats_F2)

Compare variability of depth within individuals.

p1 <- ggplot(idepth, aes(x = MAX, y = TOTAL)) +
  geom_smooth(method = "auto",linetype = "dashed", size = 1, color = "darkred") +
  geom_point(shape = 1) +
  theme_standard
p2 <- ggplot(idepth, aes(x = TOTAL, y = VAR)) +
  geom_smooth(method = "auto",linetype = "dashed", size = 1, color = "darkred") +
  geom_point(shape = 1) +
  theme_standard
p3 <- ggplot(idepth, aes(MAX, MEDIAN)) +
  geom_smooth(method = "auto",linetype = "dashed", size = 1, color = "darkred") +
  geom_point(shape = 1) +
  theme_standard
p4 <- ggplot(idepth, aes(x = MEAN, y = MEDIAN)) +
  geom_smooth(method = "auto",linetype = "dashed", size = 1, color = "darkred") +
  geom_point(shape = 1) +
  theme_standard
p5 <- ggplot(idepth, aes(x = COEFF_VAR, y = RATIO_MEAN_MEDIAN)) +
  geom_smooth(method = "auto",linetype = "dashed", size = 1, color = "darkred") +
  geom_point(shape = 1) +
  theme_standard
p6 <- ggplot(idepth, aes(MEAN)) +
  geom_histogram(binwidth = 5, color = "black", fill = "darkorange") +
  geom_vline(xintercept = 15, color = "darkred", linetype = "dashed") +
  theme_standard
p7 <- ggplot(idepth, aes(MEDIAN)) +
  geom_histogram(binwidth = 5, color = "black", fill = "darkorange") +
  geom_vline(xintercept = 10, color = "darkred", linetype = "dashed") +
  theme_standard
p8 <- ggplot(idepth, aes(RATIO_MEAN_MEDIAN)) +
  geom_histogram(binwidth = 0.1, color = "black", fill = "darkorange") +
  theme_standard
p9 <- ggplot(idepth, aes(COEFF_VAR)) +
  geom_histogram(binwidth = 0.025, color = "black", fill = "darkorange") +
  theme_standard
p10 <- ggplot(idepth, aes(x = COEFF_VAR, y = MISS_CLE.F2)) +
  geom_point(shape = 1) +
  geom_hline(yintercept = 0.25, linetype = "dashed", color = "darkred", size = 0.75) +
  theme_standard
m3b <- multiplot(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, cols=2)

Flag LQ individuals.

LQ_depth <- idepth %>%
 filter(MEAN <= 10 | MEDIAN <= 5) %>%
 select(INDV)
# LQ_miss <- read_table2("data/VCF/CLE.F3a.idepth") %>%
#   filter(N_SITES < 112000) %>%
#  select(INDV)
# 
# LQ_ind <- bind_rows(LQ_depth, LQ_miss)
LQ_depth
 
write.table(LQ_depth, "data/VCF/F3_LQ.indv",
           col.names = TRUE, row.names = FALSE, quote = FALSE)

Remove flagged loci and individuals.


vcftools --vcf data/VCF/temp/CLE.F3a.recode.vcf --out data/VCF/temp/CLE.F3 --remove data/VCF/F3_LQ.indv --recode --recode-INFO-all

vcftools --vcf data/VCF/temp/CLE.F3.recode.vcf --out data/VCF/F3 --keep data/VCF/duplicate.ind --012
vcftools --vcf data/VCF/temp/CLE.F3.recode.vcf --out data/VCF/F3 --keep data/VCF/duplicate.ind --geno-depth

vcftools --vcf data/VCF/temp/CLE.F3.recode.vcf --out data/VCF/CLE.F3 --depth
vcftools --vcf data/VCF/temp/CLE.F3.recode.vcf --out data/VCF/CLE.F3 --site-mean-depth
vcftools --vcf data/VCF/temp/CLE.F3.recode.vcf --out data/VCF/CLE.F3 --missing-indv
vcftools --vcf data/VCF/temp/CLE.F3.recode.vcf --out data/VCF/CLE.F3 --missing-site
vcftools --vcf data/VCF/temp/CLE.F3.recode.vcf --out data/VCF/CLE.F3 --het

Compare stats post-filtering:

# load stats files ----
ind_stats_F3 <- read.ind.stats(dir = "data/VCF", vcf = "CLE.F3") %>%
  separate(INDV, into = c("SP", "LIB", "SAMPLE_ID"), sep = "_", remove = FALSE)
loc_stats_F3 <- read.loc.stats(dir = "data/VCF/", vcf = "CLE.F3")
# plot missing data per indv ----
p1 <- ggplot(ind_stats_F3, aes(x = MISS_CLE.F3)) +
  geom_histogram(binwidth = .01, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MISS_CLE.F3, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0.25),
             color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "missing data per indv") +
  theme_standard
# plot read depth per indv ----
p2 <- ggplot(ind_stats_F3, aes(x = MEAN_DEPTH_CLE.F3)) +
  geom_histogram(binwidth = 10, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE.F3, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
             color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean read depth per indv") +
  theme_standard
# plot depth vs missing ----
p3 <- ggplot(ind_stats_F3, aes(x = MEAN_DEPTH_CLE.F3, y = MISS_CLE.F3)) +
  geom_point() +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE.F3, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
             color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(MISS_CLE.F3, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 0.25),
             color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean depth per indv", y = "% missing data") +
  theme_standard
# plot distribution missing data per locus ----
p4 <- ggplot(loc_stats_F3, aes(x = MISS_CLE.F3)) +
  geom_histogram(binwidth = 0.01, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MISS_CLE.F3, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0.1),
             color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "% missing data per locus") +
  theme_standard
# plot distribution mean read depth ----
p5 <- ggplot(loc_stats_F3, aes(x = MEAN_DEPTH_CLE.F3)) +
  geom_histogram(binwidth = 20, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE.F3, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
             color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean read depth per locus") +
  theme_standard
# plot read depth vs missing data ----
p6 <- ggplot(loc_stats_F3, aes(x = MEAN_DEPTH_CLE.F3, y = MISS_CLE.F3)) +
  geom_point() +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE.F3, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
             color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(MISS_CLE.F3, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 0.1),
             color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean depth per locus", y = "% missing data") +
  theme_standard
m3 <- multiplot(p1, p2, p3, p4, p5, p6, cols=2)

Filter 4: Allele balance

AB: Allele balance at heterozygous sites: a number between 0 and 1 representing the ratio of reads showing the reference allele to all reads, considering only reads from individuals called as heterozygous

Allele balance is the ratio of reads for reference allele to all reads, considering only reads from individuals called as heterozygous. Values range from 0 - 1; allele balance (for real loci) should be approx. 0.5. Filter contigs SNPs for which the with allele balance < 0.25 and > 0.75.

read.table("data/VCF/temp/CLE.F4.AB",
           col.names = "AB", stringsAsFactors = FALSE) %>%
  ggplot(aes(x = AB)) +
  geom_histogram(binwidth = 0.01, color = "black", fill = "darkorange") +
  geom_vline(xintercept = 0.5, color = "red", linetype = "dashed", size = 1) +
  geom_vline(xintercept = 0.25, color = "darkblue", linetype = "dashed", size = 1) +
  geom_vline(xintercept = 0.75, color = "darkblue", linetype = "dashed", size = 1) +
  theme_standard

Filter contigs with SNP calls with AB > 0.25, AB > 0.75; retain loci very close to 0 (retain loci that are fixed variants). Remove genotypes if the quality sum of the reference or alternate allele was 0.

Remaining SNPs: 65,837.

Filter 5: quality/depth ratio


# site depth
cut -f8 CLE.F4.vcf | grep -oe "DP=[0-9]*" | sed -s 's/DP=//g' > CLE.4.DEPTH

# quality score
mawk '!/#/'  CLE.F4.vcf | cut -f1,2,6 > CLE.F4.loci.qual

Compare quality/depth ratio.

# depth
depth <- read.table("data/VCF/temp/CLE.4.DEPTH",
                    col.names = "depth")
# quality score
qual <- read.table("data/VCF/temp/CLE.F4.loci.qual",
                   col.names = c("locus", "pos", "qual"))
temp <- bind_cols(qual, depth) %>%
  mutate(ratio = qual/depth)
ggplot(temp, aes(x = ratio)) +
  geom_histogram(binwidth = 0.25, color = "black", fill = "grey85") +
  geom_vline(xintercept = 0.2, color = "darkred", linetype = "dashed", size = 1) +
  theme_standard

Remove loci with quality/depth ratio < 0.2


vcffilter -s -f "QUAL / DP > 0.2" CLE.F4.vcf > CLE.F5.vcf 

mawk '!/#/' CLE.F5.vcf | wc -l

Remaining SNPs: 38,334

Filter 6: mapping quality

Remove loci based on ratio of mapping quality for reference and alternate allele, i.e. sites that have a high discrepancy between the mapping qualities of two alleles.

temp <- read.table("data/VCF/temp/CLE.F5.MQM", col.names = "MQM")
mapqual <- read.table("data/VCF/temp/CLE.F5.MQMR", col.names = "MQMR")
mapqual <- bind_cols(mapqual, temp) %>%
  mutate(ratio = MQM/MQMR)
filter <- mapqual %>%
  filter(ratio < 0.25 | ratio > 1.75)
ggplot(mapqual, aes(x = MQM, y = MQMR)) +
  geom_point(shape = 1, size = 1) + 
  geom_abline(intercept = 0, slope = 1, size = 1, color = "red", linetype = "dashed") +
  geom_abline(intercept = 0, slope = 4, size = 1, color = "darkblue", linetype = "dashed") +
  geom_abline(intercept = 0, slope = 0.571, size = 1, color = "darkblue", linetype = "dashed") +
  geom_point(data = filter, aes(x = MQM, y = MQMR), shape = 21, color = "black", fill = "red") +
  scale_x_continuous(limits = c(0, 65)) +
  scale_y_continuous(limits = c(0, 65)) +
  theme_standard

Filter loci with mapping quality ratio < 0.25 and > 1.75.


vcffilter -s -f "MQM / MQMR > 0.25 & MQM / MQMR < 1.75" CLE.F5.vcf > CLE.F6.vcf

mawk '!/#/' CLE.F6.vcf | wc -l

Remaining SNPs: 36,633.

Filter 7: Strand balance

SRF: Number of reference observations on the forward strand SAF: Number of alternate observations on the forward strand SRR: Number of reference observations on the reverse strand SAR: Number of alternate observations on the reverse strand

Paired end reads should not overlap, and a SNP site should only be covered by either the forward or reverse read (strand).

SAF <- read.table("data/VCF/temp/CLE.F6.SAF",
                  col.names = "SAF")
SAR <- read.table("data/VCF/temp/CLE.F6.SAR",
                  col.names = "SAR")
strands <- bind_cols(SAF, SAR)
SRF <- read.table("data/VCF/temp/CLE.F6.SRF",
                  col.names = "SRF")
strands <- bind_cols(strands, SRF)
SRR <- read.table("data/VCF/temp/CLE.F6.SRR",
                  col.names = "SRR")
strands <- bind_cols(strands, SRR) %>%
  mutate(ratioA = SAF/SAR, ratioR = SRF/SRR)
ggplot(strands, aes(x = SAF, y = SAR)) +
  geom_point(shape = 1) +
  geom_abline(intercept = 0, slope = 0.1, color = "darkblue", linetype = "dashed", size = 1) +
  geom_abline(intercept = 0, slope = 100, color = "darkblue", linetype = "dashed", size = 1) +
  theme_standard

Remove SNP sites that have > 100x more forward alternate reads than reverse alternate reads and > 100x more forward reverse reads than reverse alternate reads.


vcffilter -f "SAF / SAR > 100 & SRF / SRR > 100 | SAR / SAF > 100 & SRR / SRF > 100" -s CLE.F6.vcf > CLE.F7.vcf

mawk '!/#/' CLE.F7.vcf | wc -l

Number of SNPs remaining: 34,587.

Filter 8: Properly paired status

PAIRED: Proportion of observed alternate alleles which are supported by properly paired read fragments PAIREDR: Proportion of observed reference alleles which are supported by properly paired read fragments

Identify loci with only unpaired reads mapping to them - an artifact introduced due de novo reference assembly. Compare number of paired reads mapping the reference and the alternate alleles to identify discrepancy in the paired status for reads supporting reference and alternate alleles.

Number of SNPs in data set: 32,445.

Filter 9: Maximum depth & Quality

Identify distribution of depth (based on original data set) to identify loci with excess coverage.

Original number of individuals in data set is 256 (INFO flags in filtered data set are are based on original number of individuals in data set).

Create file with the original site depth and quality score for each site:

Calculate average depth and standard deviation:

# depth
depth <- read.table("data/VCF/temp/CLE.F8.DEPTH",
                    col.names = "depth")
# quality score
qual <- read.table("data/VCF/temp/CLE.F8.loci.qual",
                   col.names = c("locus", "pos", "qual"))
# mean depth
mean_depth <- mean(depth$depth)
# standard deviation
std <- sd(depth$depth)
# calculate cutoff
cutoff <- sum(mean_depth + (2*std))
# identify SNPs with excess (i.e. depth > mean depth + 1 standard deviation
# and quality score < 2x the depth at that site
df <- bind_cols(qual, depth) %>%
  mutate(qualcutoff = 2*depth)
removeloc <- df %>%
  filter(depth > cutoff) %>%
  filter(qual < 2*depth)
# plot
ggplot(df, aes(x = depth, y = qual)) +
  geom_point(shape = 1) +
  geom_point(data = removeloc, aes(x = depth, y = qual), shape = 21, color = "black", fill = "red") +
  geom_line(data = df, aes(x = depth, y = qualcutoff), color = "blue",  linetype = "dashed", size = 1) +
  geom_vline(xintercept = cutoff, color = "blue", linetype = "dashed", size = 1) +
  theme_standard

LQ <- removeloc %>%
  select(locus, pos)
write.table(LQ, "data/VCF/temp/LQ_F9.loci", 
            col.names = FALSE, row.names = FALSE, quote = FALSE)

Mean depth per locus (across all indivuals) is 2.58828610^{4} and the standard deviation is 1.401478610^{4}.

Filter SNP site with depth > mean depth + 1 standard deviation = 5.391243210^{4} and that have quality scores < 2x the depth at that site and output depth per site.

Compare the distribution of mean depth per site averaged across individuals to determine cut-off value of sites with excessively high depth indicative of paralogs/multicopy loci.

# calculate mean depth per site (177 individuals)
read.table("data/VCF/CLE.F9a.ldepth.mean",
           header = TRUE, stringsAsFactors = FALSE) %>% 
  ggplot(aes(x = MEAN_DEPTH)) +
  geom_histogram(binwidth = 20, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = quantile(MEAN_DEPTH, .95)),
                 color = "darkblue", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = quantile(MEAN_DEPTH, .99)),
                 color = "darkblue", linetype = "dashed", size = 1) +
  scale_y_sqrt() +
  labs(x = "mean depth per site") +
  theme_standard

Choose cut-off for maximum mean read depth = 250.


vcftools --vcf  data/VCF/temp/CLE.F8.vcf --out data/VCF/temp/CLE.F9 --max-meanDP 180 --exclude-positions data/VCF/temp/LQ_F9.loci --recode --recode-INFO-all 

vcftools --vcf data/VCF/temp/CLE.F9.recode.vcf --out data/VCF/F9 --keep data/VCF/duplicate.ind --012
vcftools --vcf data/VCF/temp/CLE.F9.recode.vcf --out data/VCF/F9 --keep data/VCF/duplicate.ind --geno-depth

vcftools --vcf data/VCF/temp/CLE.F9.recode.vcf --out data/VCF/CLE.F9 --depth
vcftools --vcf data/VCF/temp/CLE.F9.recode.vcf --out data/VCF/CLE.F9 --site-mean-depth
vcftools --vcf data/VCF/temp/CLE.F9.recode.vcf --out data/VCF/CLE.F9 --missing-indv
vcftools --vcf data/VCF/temp/CLE.F9.recode.vcf --out data/VCF/CLE.F9 --missing-site
vcftools --vcf data/VCF/temp/CLE.F9.recode.vcf --out data/VCF/CLE.F9 --het

Depth distribution per locus after filtering:

read_table2("data/VCF/CLE.F9.ldepth.mean") %>%
  ggplot(aes(x = MEAN_DEPTH)) +
  geom_histogram(binwidth = 10, color = "black", fill = "darkorange") +
  theme_standard

Analyze stats post-filtering:

# load stats files ----
ind_stats_F9 <- read.ind.stats(dir = "data/VCF", vcf = "CLE.F9") %>%
  separate(INDV, into = c("SP", "LIB", "SAMPLE_ID"), sep = "_", remove = FALSE)
loc_stats_F9 <- read.loc.stats(dir = "data/VCF/", vcf = "CLE.F9")

Data set contains 30920 SNP sites and 242 individuals.

Filter 10: Remove Indels

Remove Indels from data set.


vcfallelicprimitives data/VCF/temp/CLE.F9.recode.vcf --keep-info --keep-geno > data/VCF/temp/CLE.prim.vcf

vcftools --vcf data/VCF/temp/CLE.prim.vcf --out data/VCF/temp/CLE.F10 --remove-indels --recode --recode-INFO-all

vcftools --vcf data/VCF/temp/CLE.F10.recode.vcf --out data/VCF/F10 --keep data/VCF/duplicate.ind --012
vcftools --vcf data/VCF/temp/CLE.F10.recode.vcf --out data/VCF/F10 --keep data/VCF/duplicate.ind --geno-depth

vcftools --vcf data/VCF/temp/CLE.F10.recode.vcf --out data/VCF/CLE.F10 --depth
vcftools --vcf data/VCF/temp/CLE.F10.recode.vcf --out data/VCF/CLE.F10 --site-mean-depth
vcftools --vcf data/VCF/temp/CLE.F10.recode.vcf --out data/VCF/CLE.F10 --missing-indv
vcftools --vcf data/VCF/temp/CLE.F10.recode.vcf --out data/VCF/CLE.F10 --missing-site
vcftools --vcf data/VCF/temp/CLE.F10.recode.vcf --out data/VCF/CLE.F10 --het

Analyze stats post-filtering:

# load stats files ----
ind_stats_F10 <- read.ind.stats(dir = "data/VCF", vcf = "CLE.F10") %>%
  separate(INDV, into = c("SP", "LIB", "SAMPLE_ID"), sep = "_", remove = FALSE)
loc_stats_F10 <- read.loc.stats(dir = "data/VCF/", vcf = "CLE.F10")
# plot missing data per indv ----
p1 <- ggplot(ind_stats_F10, aes(x = MISS_CLE.F10)) +
  geom_histogram(binwidth = .01, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MISS_CLE.F10, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0.25),
             color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "missing data per indv") +
  theme_standard
# plot read depth per indv ----
p2 <- ggplot(ind_stats_F10, aes(x = MEAN_DEPTH_CLE.F10)) +
  geom_histogram(binwidth = 10, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE.F10, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
             color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean read depth per indv") +
  theme_standard
# plot depth vs missing ----
p3 <- ggplot(ind_stats_F10, aes(x = MEAN_DEPTH_CLE.F10, y = MISS_CLE.F10)) +
  geom_point() +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE.F10, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
             color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(MISS_CLE.F10, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 0.25),
             color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean depth per indv", y = "% missing data") +
  theme_standard
# plot distribution missing data per locus ----
p4 <- ggplot(loc_stats_F10, aes(x = MISS_CLE.F10)) +
  geom_histogram(binwidth = 0.01, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MISS_CLE.F10, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0.1),
             color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "% missing data per locus") +
  theme_standard
# plot distribution mean read depth ----
p5 <- ggplot(loc_stats_F10, aes(x = MEAN_DEPTH_CLE.F10)) +
  geom_histogram(binwidth = 5, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE.F10, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
             color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean read depth per locus") +
  theme_standard
# plot read depth vs missing data ----
p6 <- ggplot(loc_stats_F10, aes(x = MEAN_DEPTH_CLE.F10, y = MISS_CLE.F10)) +
  geom_point() +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE.F10, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
             color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(MISS_CLE.F10, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 0.1),
             color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean depth per locus", y = "% missing data") +
  theme_standard
m3 <- multiplot(p1, p2, p3, p4, p5, p6, cols=2)

Data set contains 30920 SNP sites and 242 individuals.

Filter 11: Missing data and minimum depth per locus

Assess individuals in terms of low coverage, high missing data, and frequency burden (i.e. distribution of the number of variants within each individual of a specific frequency).


vcftools --vcf data/VCF/temp/CLE.F10.recode.vcf --out data/VCF/temp/CLE.F11 --min-meanDP 15 --max-missing 0.9 --recode --recode-INFO-all

vcftools --vcf data/VCF/temp/CLE.F11.recode.vcf --out data/VCF/F11 --keep data/VCF/duplicate.ind --012
vcftools --vcf data/VCF/temp/CLE.F11.recode.vcf --out data/VCF/F11 --keep data/VCF/duplicate.ind --geno-depth

vcftools --vcf data/VCF/temp/CLE.F11.recode.vcf --out data/VCF/CLE.F11 --depth
vcftools --vcf data/VCF/temp/CLE.F11.recode.vcf --out data/VCF/CLE.F11 --site-mean-depth
vcftools --vcf data/VCF/temp/CLE.F11.recode.vcf --out data/VCF/CLE.F11 --missing-indv
vcftools --vcf data/VCF/temp/CLE.F11.recode.vcf --out data/VCF/CLE.F11 --missing-site
vcftools --vcf data/VCF/temp/CLE.F11.recode.vcf --out data/VCF/CLE.F11 --het
vcftools --vcf data/VCF/temp/CLE.F11.recode.vcf --out data/VCF/CLE.F11 --hardy

Filter 12: Excess heterozygosity

Identify SNPs with more than 0.5 heterozygosity and significant.

# calculate observed heterozygosity
hwe <- read.table("data/VCF/CLE.F11.hwe", 
                  stringsAsFactors = FALSE, header = TRUE) %>%
  select(-ChiSq_HWE) %>%
  separate(`OBS.HOM1.HET.HOM2.`, 
           into = c("obs_hom1", "obs_het", "obs_hom2"), 
           sep = "/", convert = TRUE) %>%
  separate(`E.HOM1.HET.HOM2.`, 
           into = c("exp_hom1", "exp_het", "exp_hom2"), 
           sep = "/", convert = TRUE) %>%   
  mutate(Ho = obs_het/(obs_hom1 + obs_hom2 + obs_het),
         He = exp_het/(exp_hom1 + exp_hom2 + exp_het)) %>%
  select(CHR, POS, obs_hom1, exp_hom1, obs_hom2, exp_hom2, P_HET_DEFICIT, obs_het, exp_het, P_HET_EXCESS, Ho, He, P_HWE)

Distribution of observed heterozygosity.

ggplot(hwe, aes(x = Ho)) + 
  geom_histogram(binwidth = 0.05, color = "black", fill = "darkorange") +
  geom_vline(xintercept = 0.5, color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "observed heterozygosity") +
  theme_standard

Identify loci with heterozygosity > 0.5, then correct p-values for multiple comparisons using Benjamini-Hochberg method.

# identify contigs with SNPs with Ho > 0.5 & significant
excess_hwe <- hwe %>%
  filter(Ho > 0.5) %>%
  mutate(p_adj = p.adjust(P_HET_EXCESS), method = "fdr") %>%
  filter(p_adj < 0.01)
contigs <- hwe %>%
  filter(CHR %in% excess_hwe$CHR) %>%
  select(CHR) %>%
  unique()
# identify all SNPs on contigs
SNPs <- hwe %>%
  filter(CHR %in% contigs$CHR) %>%
  select(CHR, POS)
# View(contigs)
# Write contig/position to text file, use file with vcftools to remove positions from dataset 
write.table(SNPs, file = "data/VCF/hetexcess.loci", 
            col.names= FALSE, row.names = FALSE, quote = FALSE)

Remove SNPs with excess heterozygosity and contigs with more than one SNP w/excess heterozygosity.


vcftools --vcf data/VCF/temp/CLE.F11.recode.vcf --out data/VCF/temp/CLE.F12 --exclude-positions data/VCF/hetexcess.loci --recode --recode-INFO-all

vcftools --vcf data/VCF/temp/CLE.F12.recode.vcf --out data/VCF/F12 --keep data/VCF/duplicate.ind --012
vcftools --vcf data/VCF/temp/CLE.F12.recode.vcf --out data/VCF/F12 --keep data/VCF/duplicate.ind --geno-depth

vcftools --vcf data/VCF/temp/CLE.F12.recode.vcf --out data/VCF/CLE.F12 --depth
vcftools --vcf data/VCF/temp/CLE.F12.recode.vcf --out data/VCF/CLE.F12 --site-mean-depth
vcftools --vcf data/VCF/temp/CLE.F12.recode.vcf --out data/VCF/CLE.F12 --missing-indv
vcftools --vcf data/VCF/temp/CLE.F12.recode.vcf --out data/VCF/CLE.F12 --missing-site
vcftools --vcf data/VCF/temp/CLE.F12.recode.vcf --out data/VCF/CLE.F12 --het
vcftools --vcf data/VCF/temp/CLE.F12.recode.vcf --out data/VCF/CLE.F12 --geno-depth

Visualize stats:

# load stats files ----
ind_stats_F12 <- read.ind.stats(dir = "data/VCF", vcf = "CLE.F12") %>%
  separate(INDV, into = c("SP", "LIB", "SAMPLE_ID"), sep = "_", remove = FALSE)
loc_stats_F12 <- read.loc.stats(dir = "data/VCF/", vcf = "CLE.F12")

Data set contains 29258 SNP sites and 242 individuals.

Filter 13: Filter LQ individuals

Compare mean depth and number of sites called per individual.

read_table2("data/VCF/CLE.F12.idepth") %>%
  ggplot(aes(x = N_SITES, y = MEAN_DEPTH)) +
  geom_point(shape = 1, size = 1) +
  labs(x = "number of sites", y = "mean read depth") +
  theme_standard

Use genotype depth file to identify individuals with high variance in read depth across loci.

Compare distribution of depth.

# would need to join with Sample Info to get pop specifications
ggplot(gdepth, aes(x = INDV, y = CHROM)) +
  geom_tile(aes(fill = GDEPTHBIN)) +
  scale_fill_viridis(direction = -1, option = "viridis",
                     name = 'genotype depth',
                     discrete = TRUE) +
  facet_grid(. ~ POP, space = "free", scales = "free", drop = TRUE) +
  labs(x = "individual", y = "locus") +
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks.y = element_blank(),
        legend.position = "bottom")

Compare distribution of depth of individuals grouped by library.

ggplot(gdepth, aes(x = INDV, y = CHROM)) + 
  geom_tile(aes(fill = GDEPTHBIN)) + 
  scale_fill_viridis(direction = -1, option = "viridis",
                     name = 'genotype depth',
                     discrete = TRUE) +
  facet_grid(. ~ LIB, space = "free", scales = "free", drop = TRUE) +
  labs(x = "individual", y = "locus") +
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks.y = element_blank(),
        legend.position = "bottom")

Compare distribution of genotype depths (across all individuals).

temp <- count(gdepth, GDEPTHBIN)
ggplot(temp, aes(x = GDEPTHBIN, y = n)) +
  geom_bar(stat= "identity", color = "black", fill = "darkorange") +
  theme_standard

Identify variance in depth w/in individuals.

# calculate summary statistics
idepth <- gdepth %>%
  group_by(INDV) %>%
  summarize(TOTAL = sum(DEPTH),
            MAX = max(DEPTH),
            MEAN = mean(DEPTH),
            MEDIAN = median(DEPTH),
            VAR = var(DEPTH),
            STD = sd(DEPTH)) %>%
  mutate(COEFF_VAR = STD/MEAN,
         RATIO_MEAN_MEDIAN = MEAN/MEDIAN)
p1 <- ggplot(idepth, aes(x = MEAN, y = MEDIAN)) +
  geom_smooth(method = "auto",linetype = "dashed", size = 1, color = "darkred") +
  geom_point(shape = 1) +
  theme_standard
p2 <- ggplot(idepth, aes(x = MAX, y = TOTAL)) +
  geom_smooth(method = "lm",linetype = "dashed", size = 1, color = "darkred") +
  geom_point(shape = 1) +
  theme_standard
p3 <- ggplot(idepth, aes(x = TOTAL, y = VAR)) +
  geom_smooth(method = "auto",linetype = "dashed", size = 1, color = "darkred") +
  geom_point(shape = 1) +
  theme_standard
p4 <- ggplot(idepth, aes(MEDIAN)) +
  geom_histogram(binwidth = 10, color = "black", fill = "darkorange") +
  theme_standard
p5 <- ggplot(idepth, aes(RATIO_MEAN_MEDIAN)) +
  geom_histogram(binwidth = 0.1, color = "black", fill = "darkorange") +
  theme_standard
p6 <- ggplot(idepth, aes(COEFF_VAR)) +
  geom_histogram(binwidth = 0.05, color = "black", fill = "darkorange") +
  theme_standard
m14b <- multiplot(p1, p2, p3, p4, p5, p6, cols=2)

Compare depth, missing data and individual heterozygosity levels.

imiss <- read_table2("data/VCF/CLE.F12.imiss") %>%
  select(INDV, N_DATA, F_MISS)
istats <- left_join(idepth, imiss)
Fis <- read_table2("data/VCF/CLE.F12.het") %>%
  select(-N_SITES)
istats <- left_join(istats, Fis)
p1 <- ggplot(istats, aes(x = MEAN, y = F_MISS, fill = `F`)) +
  geom_point(shape = 21, size = 2, color = "black") +
  scale_fill_viridis(direction = 1, option = "viridis",
                     name = 'Fis', discrete = FALSE) +
  theme_standard
p2 <- ggplot(istats, aes(x = COEFF_VAR, y = MEAN, fill = F_MISS)) +
 geom_point(shape = 21, size = 2, color = "black") +
  scale_fill_viridis(direction = -1, option = "viridis",
                     name = '% missing', discrete = FALSE) +
  theme_standard
p3 <- ggplot(istats, aes(x = `O(HOM)`, y = MEAN, fill = F_MISS)) +
  geom_point(shape = 21, size = 2, color = "black") +
  scale_fill_viridis(direction = -1, option = "viridis",
                     name = 'mean read depth', discrete = FALSE) +
  theme_standard
p4 <- ggplot(istats, aes(x = MEAN)) +
  geom_histogram(binwidth = 10, color = "black", fill = "darkorange") +
  theme_standard
p5 <- ggplot(istats, aes(x = `F`, y = MEAN, fill = F_MISS)) +
  geom_point(shape = 21, size = 2, color = "black") +
  scale_fill_viridis(direction = -1, option = "viridis",
                     name = '% missing', discrete = FALSE) +
  theme_standard
p6 <- ggplot(istats, aes(x = `F`, y = COEFF_VAR, fill = F_MISS)) +
  geom_point(shape = 21, size = 2, color = "black") +
  scale_fill_viridis(direction = -1, option = "viridis",
                     name = '% missing', discrete = FALSE) +
  theme_standard
p7 <- ggplot(istats, aes(x = `F`, y = RATIO_MEAN_MEDIAN, fill = F_MISS)) +
  geom_point(shape = 21, size = 2, color = "black") +
  scale_fill_viridis(direction = -1, option = "viridis",
                     name = '% missing', discrete = FALSE) +
  scale_y_continuous(limits = c(0.8, 4)) +
  theme_standard
p8 <- ggplot(istats, aes(x = F_MISS)) +
  geom_histogram(binwidth = 0.01, color = "black", fill = "darkorange") +
  theme_standard
m14b <- multiplot(p1, p2, p3, p4, p5, p6, p7, p8, cols=2)

Flag low quality individuals

View(istats)
LQ_ind <- istats %>%
   filter(`F` > .9 | `F` < -.3) %>%
   select(INDV)
write.table(LQ_ind, "data/VCF/F13_LQ.indv",
            col.names = TRUE, row.names = FALSE, quote = FALSE)

Filter 14: Missing data by sample location


vcftools --vcf data/VCF/temp/CLE.F12.recode.vcf --out data/VCF/temp/CLE.F13 --remove data/VCF/F13_LQ.indv --recode --recode-INFO-all

vcftools --vcf data/VCF/temp/CLE.F13.recode.vcf --out data/VCF/F13 --keep data/VCF/duplicate.ind --012
vcftools --vcf data/VCF/temp/CLE.F13.recode.vcf --out data/VCF/F13 --keep data/VCF/duplicate.ind --geno-depth

# Aransas Bay
vcftools --vcf data/VCF/temp/CLE.F13.recode.vcf --out data/VCF/temp/ARA --keep data/VCF/ARA.ind  --recode --recode-INFO-all
vcftools --vcf data/VCF/temp/ARA.recode.vcf --out data/VCF/ARA --missing-site

# Corpus Christi Bay
vcftools --vcf data/VCF/temp/CLE.F13.recode.vcf --out data/VCF/temp/CC --keep data/VCF/CC.ind  --recode --recode-INFO-all
vcftools --vcf data/VCF/temp/CC.recode.vcf --out data/VCF/CC --missing-site

# Galveston Bay
vcftools --vcf data/VCF/temp/CLE.F13.recode.vcf --out data/VCF/temp/GAL --keep data/VCF/GAL.ind  --recode --recode-INFO-all
vcftools --vcf data/VCF/temp/GAL.recode.vcf --out data/VCF/GAL --missing-site

# Matagorda Bay
vcftools --vcf data/VCF/temp/CLE.F13.recode.vcf --out data/VCF/temp/MAT --keep data/VCF/MAT.ind  --recode --recode-INFO-all
vcftools --vcf data/VCF/temp/MAT.recode.vcf --out data/VCF/MAT --missing-site

# San Antonio Bay
vcftools --vcf data/VCF/temp/CLE.F13.recode.vcf --out data/VCF/temp/SA --keep data/VCF/SA.ind  --recode --recode-INFO-all
vcftools --vcf data/VCF/temp/SA.recode.vcf --out data/VCF/SA --missing-site

# Sabine Lake 
vcftools --vcf data/VCF/temp/CLE.F13.recode.vcf --out data/VCF/temp/SL --keep data/VCF/SL.ind  --recode --recode-INFO-all
vcftools --vcf data/VCF/temp/SL.recode.vcf --out data/VCF/SL --missing-site

Compare missing data per sample region:

identify loci with high missing data in each sample location

# identify loci with high missing data in each region
SNPs <- filter(loc_missing, F_MISS > 0.1) 
count(SNPs, POP)
contigs <- SNPs %>%
  distinct(CHR)
SNPs <- SNPs %>%
  select(CHR, POS)
# Write contig/position to text file, use file with vcftools to remove positions from dataset 
write.table(SNPs, file = "data/VCF/LQ_F13.loci", 
            col.names= FALSE, row.names = FALSE, quote = FALSE)

Remove loci from data set:


vcftools --vcf data/VCF/temp/CLE.F13.recode.vcf --out data/VCF/temp/CLE.F14 --exclude-positions data/VCF/LQ_F13.loci --recode --recode-INFO-all

vcftools --vcf data/VCF/temp/CLE.F14.recode.vcf --out data/VCF/F14 --keep data/VCF/duplicate.ind --012
vcftools --vcf data/VCF/temp/CLE.F14.recode.vcf --out data/VCF/F14 --keep data/VCF/duplicate.ind --geno-depth

vcftools --vcf data/VCF/temp/CLE.F14.recode.vcf --out data/VCF/CLE.F14 --site-quality
vcftools --vcf data/VCF/temp/CLE.F14.recode.vcf --out data/VCF/CLE.F14 --depth
vcftools --vcf data/VCF/temp/CLE.F14.recode.vcf --out data/VCF/CLE.F14 --site-mean-depth
vcftools --vcf data/VCF/temp/CLE.F14.recode.vcf --out data/VCF/CLE.F14 --missing-indv
vcftools --vcf data/VCF/temp/CLE.F14.recode.vcf --out data/VCF/CLE.F14 --missing-site
vcftools --vcf data/VCF/temp/CLE.F14.recode.vcf --out data/VCF/CLE.F14 --het
vcftools --vcf data/VCF/temp/CLE.F14.recode.vcf --out data/VCF/CLE.F14 --geno-depth
vcftools --vcf data/VCF/temp/CLE.F14.recode.vcf --out data/VCF/CLE.F14 --singletons
vcftools --vcf data/VCF/temp/CLE.F14.recode.vcf --out data/VCF/CLE.F14 --indv-freq-burden
vcftools --vcf data/VCF/temp/CLE.F14.recode.vcf --out data/VCF/CLE.F14 --freq

Compare stats:

# load stats files ----
ind_stats_F14 <- read.ind.stats(dir = "data/VCF", vcf = "CLE.F14") %>%
  separate(INDV, into = c("SP", "LIB", "SAMPLE_ID"), sep = "_", remove = FALSE, extra = "merge")
loc_stats_F14 <- read.loc.stats(dir = "data/VCF", vcf = "CLE.F14")
# plot missing data per indv ----
p1 <- ggplot(ind_stats_F14, aes(x = MISS_CLE.F14)) +
  geom_histogram(binwidth = .01, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MISS_CLE.F14, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0.25),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "missing data per indv") +
  theme_standard
# plot read depth per indv ----
p2 <- ggplot(ind_stats_F14, aes(x = MEAN_DEPTH_CLE.F14)) +
  geom_histogram(binwidth = 10, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE.F14, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean read depth per indv") +
  theme_standard
# plot depth vs missing ----
p3 <- ggplot(ind_stats_F14, aes(x = MEAN_DEPTH_CLE.F14, y = MISS_CLE.F14)) +
  geom_point() +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE.F14, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(MISS_CLE.F14, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 0.25),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean depth per indv", y = "% missing data") +
  theme_standard
# plot Fis per indv ----
p4 <- ggplot(ind_stats_F14, aes(x = Fis_CLE.F14)) +
  geom_histogram(binwidth = .01, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(Fis_CLE.F14, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "Fis per indv") +
  theme_standard
# plot Fis vs missing data per indv ----
p5 <- ggplot(ind_stats_F14, aes(x = Fis_CLE.F14, y = MISS_CLE.F14)) +
  geom_point() +
  geom_vline(aes(xintercept = mean(Fis_CLE.F14, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0),
                 color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(MISS_CLE.F14, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 0.25),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "Fis per indv", y = "% missing data") +
  theme_standard
# plot Fis vs mean depth per indv ----
p6 <- ggplot(ind_stats_F14, aes(x = Fis_CLE.F14, y = MEAN_DEPTH_CLE.F14)) +
  geom_point() +
  geom_vline(aes(xintercept = mean(Fis_CLE.F14, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0),
                 color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(MEAN_DEPTH_CLE.F14, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "Fis per indv", y = "mean depth per indv") +
  theme_standard
# plot distribution missing data per locus ----
p7 <- ggplot(loc_stats_F14, aes(x = MISS_CLE.F14)) +
  geom_histogram(binwidth = 0.01, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MISS_CLE.F14, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0.1),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "% missing data per locus") +
  theme_standard
# plot distribution mean read depth ----
p8 <- ggplot(loc_stats_F14, aes(x = MEAN_DEPTH_CLE.F14)) +
  geom_histogram(binwidth = 5, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE.F14, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean read depth per locus") +
  theme_standard
# plot read depth vs missing data ----
p9 <- ggplot(loc_stats_F14, aes(x = MEAN_DEPTH_CLE.F14, y = MISS_CLE.F14)) +
  geom_point() +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE.F14, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(MISS_CLE.F14, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 0.1),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean depth per locus", y = "% missing data") +
  theme_standard
# plot depth vs SNP quality ----
site_qual <- read.table("data/VCF/CLE.F14.lqual", 
                        header = TRUE, stringsAsFactors = FALSE) %>%
  mutate(PROB = 10^(-QUAL/10))
temp <- data.frame(loc_stats_F14$MEAN_DEPTH_CLE.F14, site_qual$QUAL) %>%
  rename(depth = loc_stats_F14.MEAN_DEPTH_CLE.F14, qual = site_qual.QUAL)
p10 <- ggplot(temp, aes(x = depth, y = qual)) +
  geom_point(size = 1) +
  geom_vline(aes(xintercept = mean(depth, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(qual, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean depth per locus", y = "SNP quality") +
  theme_standard
# plot number of SNPs per contig vs. mean depth ----
temp <- loc_stats_F14 %>%
  count(CHR)
p11 <- left_join(temp, loc_stats_F14) %>%
  ggplot() +
  geom_point(aes(x = n, y = MEAN_DEPTH_CLE.F14)) +
  labs(x = "number of SNPs per contig", y = "mean depth") +
  theme_standard
# plot no of SNPs per locus ----
p12 <- loc_stats_F14 %>%
  count(CHR) %>%
  ggplot(aes(x = n)) +
  geom_histogram(binwidth = 1, color = "black", fill = "darkorange") + 
  labs(x = "number of SNPs per locus") +
  theme_standard
m15 <- multiplot(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, cols=2)

Compare number of contigs vs number of SNPs.

# number of SNPs
nrow(loc_stats_F14)
[1] 28297
# number of contigs in data set
contigs <- loc_stats_F14 %>%
  distinct(CHR)
nrow(contigs)
[1] 14992

Mean number of SNPs per contig is 1.8874733.


istats <- left_join(ind_stats_raw, ind_stats_F14)

Final thresholds values for filtered data set CLE:

  • minimum sequence quality (minQ): 20
  • minimum genotype quality (minGQ): 20
  • minimum genotype call rate per locus (geno): 90%
  • minimum genotype depth (minD): 5
  • maximum missing data per individual (missInd): 25%
  • mean minimum depth per locus (m-minD): 15
  • mean maximum depth (m-minD): 180
  • dDocent filters run for allelic balance, mapping quality, strandedness and paired status, depth/quality ratio
  • Indels removed from data set
  • minimum genotype call rate by population: 90%

Data set contains 28297 SNP sites on 14992 and 239 individuals.

Haplotyping

Prep for haplotyping

Create list of individuals retained in final vcf file:

Change so that new file is printed into Haplotyping/temp folder


vcftools --vcf data/VCF/temp/CLE.F14.recode.vcf --out data/Haplotyping/temp/CLE --recode --recode-INFO-all

vcfsamplenames data/Haplotyping/temp/CLE.recode.vcf > data/Haplotyping/temp/CLE.individuals

Use CLE-CLE.individuals to create popmap as a tab-separated file of individuals and their population designation, with one individual per line (make sure UNIX format). This file is needed to write the genepop file, if not provided the script will run through the process but not write a genepop file and place into same folder rad_haplotyper.pl will be run from.

popmap <- read.table("data/Haplotyping/temp/CLE.individuals", 
                     col.names = "INDV", stringsAsFactors = FALSE) %>%
  separate(INDV, into = c("POP", "LIB", "ID"), sep = "_", remove = FALSE, extra = "merge") %>%
  select(INDV, POP)
write.table(popmap, "data/Haplotyping/temp/popmap", 
            col.names = FALSE, row.names = FALSE, quote = FALSE)

Place all necessary files in data/Haplotyping/temp directory (bam, fastq, reference.fasta, vcf-file)

Run Haplotyper

Copy files for analysis into data/Haplotyping/-folder


cd /home/soleary/BULLSHARKS/CLE_POPGEN/data/Haplotyping/temp
rm *.fq.gz
rm *.bam*

cp /home/soleary/BULLSHARKS/CLE_POPGEN/data/Haplotyping/temp/ind_stats.out /home/soleary/BULLSHARKS/CLE_POPGEN/data/Haplotyping/

cp /home/soleary/BULLSHARKS/CLE_POPGEN/data/Haplotyping/temp/stats.out /home/soleary/BULLSHARKS/CLE_POPGEN/data/Haplotyping/

cp /home/soleary/BULLSHARKS/CLE_POPGEN/data/Haplotyping/temp/CLE.gen /home/soleary/BULLSHARKS/CLE_POPGEN/data/Haplotyping/

Haplotype filtering

Overview of haplotyping success

Comparison of the number of loci that were filtered due to excess number of missing data or suspected paralogs during the haplotyping process to those that passed. Haplotyer was run without any threshold values (other than default values). The genepop file only contains loci that passed haplotyping stage.

# import stats files generated by haplotyper
hap_stats <- read.hap.stats("data/Haplotyping/stats.out")
hap_ind_stats <- read.delim("data/Haplotyping/ind_stats.out",
                           stringsAsFactors = FALSE)
# view comparison of filtered vs. passed loci
plot.filter.status(hap_stats)

Remove filtered loci from data set.

count(hap_stats, Status == "FILTERED")
hap_stats <- remove.filtered.haps(hap_stats)

14913 loci passed haplotyping process.

plot.hap.stats(hap_stats)

Data set contains 239 individuals:

plot.ind.hap.stats(hap_ind_stats)

Identify threshold values to filter data set

Proportion of individuals haplotyped

# hap_stats <- read.hap.stats("data/Haplotyping/stats.out")
p1 <- ggplot(hap_stats, aes(x = Prop_Haplotyped)) +
  geom_histogram(binwidth = 0.025, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(Prop_Haplotyped, na.rm = TRUE)),
                 color = "darkred", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0.9),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "proportion individuals haplotyped") +
  theme_standard
p2 <- ggplot(hap_stats, aes(x = Prop_Haplotyped, y = Poss_Paralog)) +
  geom_point(shape = 21, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(Prop_Haplotyped, na.rm = TRUE)),
                 color = "darkred", linetype = "dashed", size = 1) +
  labs(x = "proportion individuals haplotyped", y = "possible paralogs") +
  theme_standard
p3 <- ggplot(hap_stats, aes(x = Prop_Haplotyped, y = `Low_Cov.Geno_Err`)) +
  geom_point(shape = 21, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(Prop_Haplotyped, na.rm = TRUE)),
                 color = "darkred", linetype = "dashed", size = 1) +
  labs(x = "proportion individuals haplotyped", y = "low coverage error") +
  theme_standard
multiplot(p1, p2, p3, cols = 3)

Flag loci successfully haplotyped in < 90% of individuals.

# number of loci called in >95% individuals
count(hap_stats, Prop_Haplotyped < 0.9)
# create vector of loci to remove (choose cut-off)
Prop_Haplotyped <- filter.loci.prop_haplotyped(hap_stats, 0.9)

42 loci were flagged as genotype call rate < 0.9.

Possible paralogs per locus

Loci are flagged as possible paralogs for an individuals when more than the expected number of haplotypes based on the SNP genotype call (homozygote, heterozygote) are detected.

ggplot(hap_stats, aes(x = Poss_Paralog)) +
  geom_histogram(binwidth = 5, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(Poss_Paralog, na.rm = TRUE)),
                 color = "darkred", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = (nrow(hap_ind_stats)*0.01)),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "possible paralogs per locus") +
  theme_standard

1 % of individuals: 2.39 5 % of individuals: 11.95

Flag loci that are flagged as potential paralogs in 3 or more individuals.

# number of loci with Poss_Paralogs
count(hap_stats, Poss_Paralog > 2)
# create vector of loci to remove (choose cut-off)
Poss_Paralogs <- filter.loci.paralogs(hap_stats, 2)

169 loci were flagged as possible paralogs.

Number of SNPs & Haplotypes per locus

Each locus varies in the number of SNPs detected which determines the number of haplotypes expected in that population.

ggplot(hap_stats, aes(x = Sites)) +
  geom_histogram(binwidth = 1, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(Sites, na.rm = TRUE)),
                 color = "darkred", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 25),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "number of SNPs per contig") +
  theme_standard

Filtering loci based on number of SNPs contained on that locus could bias the data set as loci with high recombination may be removed. On the other hand, assuming an approximate length of 250 bp loci with more than 25 SNPs would mean that 10% of bases are a polymorphisms.

p1 <- ggplot(hap_stats, aes(x = Haplotypes, y = Poss_Paralog)) +
  geom_point(shape = 21, color = "black", fill = "darkorange") +
  geom_smooth(method = "lm") +
  labs(x = "no. haplotypes", y = "possible paralogs") +
  theme_standard
p2 <- ggplot(hap_stats, aes(x = Sites, y = Poss_Paralog)) +
  geom_point(shape = 21, color = "black", fill = "darkorange") +
  geom_smooth(method = "lm") +
  labs(x = "no. of sites", y = " ") +
  theme_standard
  
multiplot(p1, p2, cols = 2)

Identify number of haplotypes loci with > 35 SNP sites.

count(hap_stats, Sites > 35)
NoSNps <- filter.loci.noSNPs(hap_stats, 35)

1 loci were flagged.

Assuming that mutation is the only mechanism resulting in new haplotypes, the maximum expected number of haplotypes per locus is number of SNPs N + 1.

p1 <- ggplot(hap_stats, aes(x = Haplotypes)) +
  geom_histogram(binwidth = 1, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(Haplotypes, na.rm = TRUE)),
                 color = "darkred", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = quantile(Haplotypes, .99, na.rm = TRUE)),
                 color = "darkred", linetype = "dashed", size = 1) +
  labs(x = "# haplotypes per locus", y = "# loci") +
  theme_standard
temp <- hap_stats %>%
  select(Locus, Sites, Haplotypes, Prop_Haplotyped, Low_Cov.Geno_Err, Poss_Paralog) %>%
  mutate(exp_sites = Sites + 1) %>%
  mutate(xtra = Haplotypes - Sites)
p2 <- ggplot(temp, aes(x = Sites, y = Poss_Paralog)) +
  geom_point() +
  geom_hline(yintercept = 5, color = "darkblue", linetype = "dashed", size = 1) +
  geom_vline(xintercept = 25, color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "# SNP sites per locus", y = "possible paralogs") +
  theme_standard
p3 <- ggplot(temp, aes(x = Sites, y = xtra)) +
  geom_point() +
  geom_hline(yintercept = 10, color = "darkblue", linetype = "dashed", size = 1) +
  geom_vline(xintercept = 25, color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "# SNP sites per locus", y = "# extra haplotypes") +
  theme_standard
p4 <- ggplot(temp, aes(x = xtra, y = Prop_Haplotyped)) +
  geom_point(size = 1) +
  geom_hline(yintercept = 0.9, color = "darkblue", linetype = "dashed", size = 1) +
  geom_vline(xintercept = 25, color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "# extra haplotypes", y = "proportion of indv haplotyped") +
  theme_standard
p5 <- ggplot(temp, aes(x = xtra, y = Low_Cov.Geno_Err)) +
  geom_point(size = 1) +
  geom_hline(yintercept = 5, color = "darkblue", linetype = "dashed", size = 1) +
  geom_vline(xintercept = 25, color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "# extra haplotypes", y = "potential genotyping error") +
  theme_standard
p6 <- ggplot(temp, aes(x = xtra, y = Poss_Paralog)) +
  geom_point(size = 1) +
  geom_hline(yintercept = 5, color = "darkblue", linetype = "dashed", size = 1) +
  geom_vline(xintercept = 25, color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "# extra haplotypes", y = "possible paralogs") +
  theme_standard
m21 <- multiplot(p1, p2, p3, p4, p5, p6, cols = 2)

Again, removing loci with unexpectedly high numbers of haplotypes may bias the data set, as loci with e.g. high recombination may be removed from the data set.

count(temp, xtra >= 50)
# create vector of loci to remove
NoHaps <- filter(temp, xtra >= 50) 
NoHaps <- NoHaps$Locus

0 loci were flagged for excessive number of haplotypes compared to the number of SNPs at that locus.

Low Coverage/Genotyping Error

After combining SNPs on the same contig during the haplotyping process, it is possible to observe fewer than the expected number of haplotypes based on the genotype call (heterozygote/homozygote). When this occurs, that genotype is coded as missing. For each locus the number of individuals for which is it flagged as a potential a potential genotyping error or suffering from null alleles due to low coverage detected for a given locus is recorded.

ggplot(hap_stats, aes(x = Low_Cov.Geno_Err)) +
  geom_histogram(binwidth = 5, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(Low_Cov.Geno_Err, na.rm = TRUE)),
                 color = "darkred", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = (nrow(hap_ind_stats)*0.05)),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "potential genotyping error") +
  theme_standard

Loci with pronounced patterns of genotyping error likely due to low coverage will have low success rate for genotyping, i.e. they will be caught in missing data filters. Coverage issues are likely genotype specfic; previous filters have targeted loci and individuals with high variance in coverage and suspect genotpes have been coded as missing, i.e. this filter need not be very conservative, regardless, loci that are repeatedly being flagged as problematic should be removed.

# summary of count of possible genotyping errors
count(hap_stats, Low_Cov.Geno_Err > 10)
# create vector of loci to remove
Geno_Err <- filter.loci.geno_err(hap_stats, 10)

56 loci were flagged as potentially affected by genotyping error.

Flag problematic individuals

Loci that are not successfully haplotyped in an individual due to missing data, complex locus, haplotyped genotype is higher/lower than SNP haplotype in a given individual are coded as missing for that individual. Problematic individual can be identified as having a high proportion of missing data.

Individuals with low proportion of successfully haplotyped loci

ggplot(hap_ind_stats, aes(x = Prop_Success)) +
  geom_histogram(binwidth = 0.01, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(Prop_Success, na.rm = TRUE)),
                 color = "darkred", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0.9),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "proportion loci successfully haplotyped") +
  theme_standard

Problem individuals can be identified by choosing a cut-off value for the minium proportion of sucessfully haplotyped loci and excluding individuals below that threshold value. Removing loci with low haplotyping success with decrease the amount of missing data. Therefore, final missing data cut-offs should be applied after removing those loci from the data set, though it is important to flag potentially problematic loci based on their haplotyping stats at this point.

# count individuals above set threshold
count(hap_ind_stats, Prop_Success < .9)
# create vector of indv to remove (choose cut-off)
PropSuccessInd <- filter.ind.prop_success(hap_ind_stats, .9)
write.table(PropSuccessInd, "data/POPGEN/propSucc.indv", 
            col.names = TRUE, row.names = FALSE, quote = FALSE, sep = "\t")
# View(as.data.frame(PropSuccessInd))

Possible paralogs per individual

Individuals with high proportion of loci flagged as possible paralogs should be flagged as potential problem individuals.

ggplot(hap_ind_stats, aes(x = Poss_Paralogs)) +
  geom_histogram(binwidth = 10, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(Poss_Paralogs, na.rm = TRUE)),
                 color = "darkred", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = (nrow(hap_stats)*0.01)),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "no of loci potential paralogs") +
  theme_standard

Cut-off for individuals with loci flagged paralogs in > 5% of loci is 745.65.

# count individuals above set threshold
count(hap_ind_stats, Poss_Paralogs > 150)
# create vector of indv to remove (choose cut-off)
Poss_ParalogInd <- filter.ind.paralogs(hap_ind_stats, 150)
write.table(Poss_ParalogInd, "data/POPGEN/possParal.indv", 
            col.names = TRUE, row.names = FALSE, quote = FALSE, sep = "\t")
# View(as.data.frame(Poss_ParalogInd))

The highest number of flagged loci in an individuals is 111, which is equivalent to 0.74% of loci.

Individuals with high proportion of potential allele dropout/genotyping error

ggplot(hap_ind_stats, aes(x = Low_Coverage.Errors)) +
  geom_histogram(binwidth = 5, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(Low_Coverage.Errors, na.rm = TRUE)),
                 color = "darkred", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = (nrow(hap_stats)*0.01)),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "# loci potential genotyping error", y = "# indv") +
  theme_standard

Remove individuals with high proportion of loci that have been flagged as potential allele dropouts/genotyping error.

# count individuals above set threshold
count(hap_ind_stats, Low_Coverage.Errors > 150)
# create vector of indv to remove (choose cut-off)
GenoErrIndv <- filter.ind.geno_err(hap_ind_stats, 150)
write.table(GenoErrIndv, "data/POPGEN/GenoErr.indv", 
            col.names = TRUE, row.names = FALSE, quote = FALSE, sep = "\t")
# View(GenoErrIndv)

The highest number of flagged loci in an individuals is 93, which is equivalent to 0.62% of loci.

Filter flagged loci and individuals as necessary

Load genepop file with haplotyped loci.

# import genepop file
gen <- read.genepop(file = "data/Haplotyping/CLE.gen",
                    ncode = 3L, quiet = FALSE)

 Converting data from a Genepop .gen file to a genind object... 


File description:  CLE.gen 

...done.
gen
/// GENIND OBJECT /////////

 // 239 individuals; 14,913 loci; 40,328 alleles; size: 46.4 Mb

 // Basic content
   @tab:  239 x 40328 matrix of allele counts
   @loc.n.all: number of alleles per locus (range: 1-52)
   @loc.fac: locus factor for the 40328 columns of @tab
   @all.names: list of allele names for each locus
   @ploidy: ploidy of each individual  (range: 2-2)
   @type:  codom
   @call: read.genepop(file = "data/Haplotyping/CLE.gen", ncode = 3L, quiet = FALSE)

 // Optional content
   @pop: population of each individual (group size range: 239-239)

Remove flagged loci and individuals.

# remove flagged loci
removeloc <- c(Poss_Paralogs, NoSNps, NoHaps, Geno_Err, Prop_Haplotyped)
gen <- genind.rem.loci(gen, removeloc)
# remove flagged individuals
removeInd <- c(Poss_ParalogInd, PropSuccessInd, GenoErrIndv)
gen <- gen.ind.rem.Ind(gen, removeInd)
gen
/// GENIND OBJECT /////////

 // 239 individuals; 14,699 loci; 39,173 alleles; size: 45.1 Mb

 // Basic content
   @tab:  239 x 39173 matrix of allele counts
   @loc.n.all: number of alleles per locus (range: 1-21)
   @loc.fac: locus factor for the 39173 columns of @tab
   @all.names: list of allele names for each locus
   @ploidy: ploidy of each individual  (range: 2-2)
   @type:  codom
   @call: .local(x = x, i = i, j = j, drop = drop)

 // Optional content
   @pop: population of each individual (group size range: 239-239)

Compare duplicate individuals


# extract genotype matrix
df <- genind2df(gen,
                usepop = TRUE,
                sep = ":", oneColPerAll = FALSE) %>%
  select(-pop) %>%
  rownames_to_column()

# create list of duplicates
pairs <- list()

# input each set of duplicates as a vector of a list
pairs[[1]] <- c("CLE_2A-A02_Cleu_Ara002", "CLE_3A-G07_Cleu_Ara002")
pairs[[2]] <- c("CLE_2A-A10_Cleu_Ara010", "CLE_2A-C07_Cleu_Ara010")
pairs[[3]] <- c("CLE_2A-B11_Cleu_Mat001", "CLE_3A-C07_Cleu_Mat001")
pairs[[4]] <- c("CLE_2A-C08_Cleu_Ara091", "CLE_2B-A01_Cleu_Ara091")
pairs[[5]] <- c("CLE_2A-F02_Cleu_San004", "CLE_3B-C07_Cleu_San004")
pairs[[6]] <- c("CLE_2A-G07_Cleu_Dic013", "CLE_3A-B03_Cleu_Dic013")
pairs[[7]] <- c("CLE_2A-G08_Cleu_Ara064", "CLE_3B-A03_Cleu_Ara064")
pairs[[8]] <- c("CLE_2B-B05_Cleu_Dic070", "CLE_3B-C08_Cleu_Dic070")
pairs[[9]] <- c("CLE_2B-B10_Cleu_Mat030", "CLE_3A-G08_Cleu_Mat030")
pairs[[10]] <- c("CLE_2B-F02_Cleu_Ara082", "CLE_3A-C08_Cleu_Ara082")
pairs[[11]] <- c("CLE_2B-G07_Cleu_Dic018", "CLE_3A-B08_Cleu_Dic018")
pairs[[12]] <- c("CLE_2B-G08_Cleu_Mat023", "CLE_3B-C03_Cleu_Mat023")
pairs[[13]] <- c("CLE_3B-G04_Cleu_Dic049", "CLE_3B-G08_Cleu_Dic049")
pairs[[14]] <- c("CLE_2A-B04_Cleu_Dic006", "CLE_3A-F06_Cleu_Ara055")
pairs[[15]] <- c("CLE_2A-E06_Cleu_Sab006", "CLE_3A-E05_Cleu_Sab015")
pairs[[16]] <- c("CLE_3B-F05_Cleu_Dic038", "CLE_3B-F09_Cleu_Dic042")
pairs[[17]] <- c("CLE_2A-B02_Cleu_Dic004", "CLE_3A-F04_Cleu_Ara053")
pairs[[18]] <- c("CLE_2A-E05_Cleu_Sab005", "CLE_3A-E04_Cleu_Sab014")
pairs[[19]] <- c("CLE_2A-A11_Cleu_Dic001", "CLE_3B-B11_Cleu_Dic033")
pairs[[20]] <- c("CLE_2A-B06_Cleu_Dic008", "CLE_3A-F07_Cleu_Ara056")
pairs[[21]] <- c("CLE_2A-B05_Cleu_Dic007", "CLE_3A-F05_Cleu_Ara054")
pairs[[22]] <- c("CLE_2A-E03_Cleu_Sab003", "CLE_3A-E02_Cleu_Sab012")
pairs[[23]] <- c("CLE_2A-B01_Cleu_Dic003", "CLE_3A-F03_Cleu_Ara052")
pairs[[24]] <- c("CLE_2A-B07_Cleu_Dic009", "CLE_3A-F08_Cleu_Ara057")
pairs[[25]] <- c("CLE_2A-E02_Cleu_Sab002", "CLE_3A-E01_Cleu_Sab011")
pairs[[26]] <- c("CLE_2A-E01_Cleu_Sab001", "CLE_3B-E12_Cleu_Sab034")
pairs[[27]] <- c("CLE_2A-E08_Cleu_Sab008", "CLE_3A-E07_Cleu_Sab017")
pairs[[28]] <- c("CLE_2A-B08_Cleu_Dic010", "CLE_3A-F09_Cleu_Ara058")
pairs[[29]] <- c("CLE_2A-E07_Cleu_Sab007", "CLE_3A-E06_Cleu_Sab016")
pairs[[30]] <- c("CLE_2A-F09_Cleu_Ara021", "CLE_2A-G05_Cleu_Ara029")

# create empty list for genotype error (discordant loci)
genoerror <- list()

# create empty vector for duplicates (retains first indv in pair)
dup <- character()

# identify discordant genotypes for each set of duplicates
for (p in pairs){

# select duplicates from genotype matrix
geno <- df %>%
  filter(rowname %in% p) %>%
  select(-rowname)

# compare genotypes
comp <- (t(geno))

contigs <- as.data.frame(comp) %>%
  rownames_to_column(var = "LOCUS") %>%
  mutate(V1 = as.character(V1),
         V2 = as.character(V2)) %>%
  filter(V1 != V2)

# write vector with first individual in pair
ind <- p[1]

dup <- c(dup, ind)

genoerror[[ind]] <- contigs

}

# if it throws error object V1 or V2 not found it means one or more of the samples names are not correct

# combine into one dataframe
genoerror <- ldply(genoerror, data.frame) %>%
  rename(DUP1 = `.id`,
         GENO_INDV1 = V1,
         GENO_INDV2 = V2)

write_delim(genoerror, "results/haplotyped.genoerror", delim = "\t")

Compare genotype depth for discordant loci:

Determine number of times discordant genotype is due to heterozygote/homozygote.

count(genoerror, GENOTYPE1 == GENOTYPE2)

Compare number of discordant genotype calls per duplicate set.

Identify loci consistently affected by genotyping error

Remove flagged loci and one individual per pair.

# remove duplicates
removeInd <- dup
gen <- gen.ind.rem.Ind(gen, removeInd)
# remove flagged loci
removeloc <- filter(per_loc, n > 3)
gen <- genind.rem.loci(gen, removeloc)
gen
/// GENIND OBJECT /////////

 // 209 individuals; 14,699 loci; 39,173 alleles; size: 40.6 Mb

 // Basic content
   @tab:  209 x 39173 matrix of allele counts
   @loc.n.all: number of alleles per locus (range: 1-21)
   @loc.fac: locus factor for the 39173 columns of @tab
   @all.names: list of allele names for each locus
   @ploidy: ploidy of each individual  (range: 2-2)
   @type:  codom
   @call: .local(x = x, i = i, j = j, loc = ..1, drop = drop)

 // Optional content
   @pop: population of each individual (group size range: 209-209)

14913 loci were removed as potentially affected by genotyping error.

Relatedness & Inbreeding

Create input file fore related package.


# need to change to R 32
library(related)

# create input file ----
df <- genind2df(gen, usepop = FALSE, oneColPerAll = TRUE) %>%
  rownames_to_column("INDV") %>%
  separate(INDV, into = c("POP", "LIB", "INDV"), sep = "_", extra = "merge") %>%
  select(-POP) %>%
  unite(IND, LIB, INDV, sep = "_")

# missing data must be 0
df[df=="NA"] <- 0

write.table(df, "scratch/related.input",
            row.names = FALSE, col.names = FALSE, sep = "\t", quote = FALSE)

# import input file as list (gdata, nloci, nalleles, ninds, freqs)
genotypedata <- readgenotypedata("scratch/related.input")

Calculate pairwise relatedness according to Lynch & Ritland 1999; uses inbreeding estimates for each individuals to estimate relatedness.


# pairwise relatedness (Lynch & Ritland 1999)
relatedness_lynchrd <- coancestry(genotypedata$gdata,
                                  lynchrd = 1)

relatedn <- relatedness_lynchrd$relatedness %>%
  select(pair.no, ind1.id, ind2.id, lynchrd)

write_delim(relatedn, "results/pairwise_relatedness")

View(ind_stats_F14)

inbreed <- relatedness_lynchrd$inbreeding %>%
  select(ind.id, L3, LH) %>%
  rename(INDV = ind.id)

write_delim(inbreed, "results/inbreeding")

Distribution of levels of pairwise relatedness:

ggplot(relatedn, aes(x = lynchrd)) +
  geom_histogram(binwidth = 0.01, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(lynchrd, na.rm = TRUE)),
             color = "darkblue", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = quantile(lynchrd, 0.95)),
             color = "darkred", linetype = "dashed", size = 1) +
  scale_y_sqrt() +
  labs(x = "relatedness", y = "number of pairs") +
  theme_standard

Distribution of levels of homozygosity:

ggplot(inbreed, aes(x = L3)) +
  geom_histogram(binwidth = 0.005, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(LH, na.rm = TRUE)),
             color = "darkblue", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = quantile(LH, 0.95)),
             color = "darkred", linetype = "dashed", size = 1) +
  labs(x = "inbreeding coefficient", y = "individuals") +
  theme_standard

Heterozygosity and HWE

Generate summary statistics

popNames(gen)
[1] "ARA"   "GAL"   "ULM"   "LMM"   "MAT"   "SL"    "SA"    "CC"    "OTHER"

Compare observed (Ho) and expected (He) heterozygosity for all individuals across all populations.

# observed heterozygosity per locus
Ho <- as.data.frame(GenDiv$Hobs) %>% 
  rownames_to_column("LOCUS") %>%
  rename(Ho = `GenDiv$Hobs`)
# expected heterozygosity per locus
Hs <- as.data.frame(GenDiv$Hexp) %>% 
  rownames_to_column("LOCUS") %>%
  rename(Hexp = `GenDiv$Hexp`)
# expected and observed heterozysity per locus
Het <- left_join(Ho, Hs, by = "LOCUS")

Identify loci that are now monomorphic and remove from data set:

monomorphic <- filter(Ho, Ho == 0)
View(monomorphic)
monomorphic <- monomorphic$LOCUS
# remove flagged loci
removeloc <- c(monomorphic)
gen <- genind.rem.loci(gen, removeloc)
gen
/// GENIND OBJECT /////////

 // 209 individuals; 14,695 loci; 39,169 alleles; size: 40.7 Mb

 // Basic content
   @tab:  209 x 39169 matrix of allele counts
   @loc.n.all: number of alleles per locus (range: 2-21)
   @loc.fac: locus factor for the 39169 columns of @tab
   @all.names: list of allele names for each locus
   @ploidy: ploidy of each individual  (range: 2-2)
   @type:  codom
   @call: .local(x = x, i = i, j = j, loc = ..1, drop = drop)

 // Optional content
   @pop: population of each individual (group size range: 1-55)
   @strata: a data frame with 16 columns ( LIB_ID, SP, WELL, SAMPLE_ID, COLLECT_YEAR, COLLECT_DATE, ... )

Expected vs observed heterozygosity across all populations.

temp <- Het %>%
  filter(Ho != 0)
# plot Ho vs Hs across all individuals
ggplot(temp, aes(x = Ho, y = Hexp)) +
  geom_point() +
  geom_abline(intercept = 0, slope = 1, color = "red", linetype = "dashed", size = 1) +
  xlim(0, 1) +
  ylim(0, 1) +
  labs(x = "observed heterozygosity Ho", y = "within cluster diversity Hs (Hexp)") +
  theme_standard

Compare distribution of observed heterozygosity (Ho) in each putative population:

Compare distribution of expected heterozygosity He, (within cluster diversity) in each putative population:

# within cluster diversity/exp diversity per locus (rows) and cluster (columns)
Hs_per <- as.data.frame(GenDiv2$Hs) %>%
  rownames_to_column("LOCUS") %>%
  rename(ARA = `1`, GAL = `2`, ULM = `3`, LMM = `4`, MAT = `5`, 
         SL = `6`, SA = `7`, CC = `8`, OTHER = `9`) %>%
  select(LOCUS, ARA, GAL, ULM, LMM, MAT, SL, SA, CC, OTHER) %>%
  gather(Group, Hs, 2:10)
# compare expected heterozygosity per locus and cluster
ggplot(Hs_per, aes(x = Group, y = Hs)) +
  geom_boxplot() +
  labs(x = "Group", y = "expected heterozygosity Ho") +
  theme_standard

Comparison of oberved vs. heterozygosity within each population:

# join data frames
Het_per <- left_join(Hs_per, Ho_per)
# plot per cluster
ggplot(Het_per, aes(x = Ho, y = Hs)) +
  geom_point(shape = 19, size = 1) +
  geom_abline(intercept = 0, slope = 1, color = "red", linetype = "dashed", size = 1) +
  xlim(0, 1) +
  ylim(0, 1) +
  facet_grid(. ~ Group) +
  labs(x = "observed heterozygosity Ho", y = "within cluster diversity Hs (Hexp)") +
  theme_standard

Test for deviations from Hardy-Weinberg equilibrium within each group (POP level):


# setPop(gen) <- ~POP
# 
# popNames(gen)
# 
# # should be able to use lapply
# HWE <- seppop(gen) %>% 
#    lapply(hw.test, B=1000)
#  
# HWE[["ALL"]] <- hw.test(gen, B = 1000)
#  
# # convert to data.frames
# hwe <- list()
#  
# for (m in names(HWE)) {
#    hwe[[m]] <- as.data.frame(HWE[[m]]) %>%
#    rename(pval = Pr.exact) %>%
#    rownames_to_column("LOCUS") %>%
#    select(LOCUS, pval)
# }
#  
# hwe <- ldply(hwe, data.frame) %>%
#    rename(POP = `.id`) %>%
#    mutate(HWE = ifelse(pval <= 0.05, "OUT", "IN"))
# 
# write_delim(hwe, "results/HWEbypop", delim = "\t")

hwe <- read_delim("results/HWEbypop", delim = "\t")

hwe_perpop <- hwe %>%
  group_by(POP) %>%
  count(HWE)

hwe_perloc <- hwe %>%
  filter(POP != "ALL") %>%
  group_by(LOCUS) %>%
  count(HWE) %>%
  filter(HWE == "OUT")

Format output and view results:

# plot data
ggplot(hwe_perpop, aes(x = HWE, y = n)) +
  geom_bar(stat = "identity", color = "black", fill = "darkorange") +
  labs(x = "significant deviation from HWE (p > 0.05)", y = "number of loci") +
  facet_grid( ~ POP) +
  theme_standard

Distribution of number of times loci are out of HWE

ggplot(hwe_perloc, aes(x = n)) +
  geom_histogram(binwidth = 1, color = "black", fill = "darkorange") +
  labs(x = "no populations out of HWE", y = "no. loci") +
  theme_standard

Remove loci out of HWE in every population:

# remove flagged loci
removeloc <- hwe_perloc %>%
  filter(n >= 5)
gen <- genind.rem.loci(gen, removeloc$LOCUS)
gen
/// GENIND OBJECT /////////

 // 209 individuals; 14,688 loci; 39,144 alleles; size: 40.7 Mb

 // Basic content
   @tab:  209 x 39144 matrix of allele counts
   @loc.n.all: number of alleles per locus (range: 2-21)
   @loc.fac: locus factor for the 39144 columns of @tab
   @all.names: list of allele names for each locus
   @ploidy: ploidy of each individual  (range: 2-2)
   @type:  codom
   @call: .local(x = x, i = i, j = j, loc = ..1, drop = drop)

 // Optional content
   @pop: population of each individual (group size range: 1-55)
   @strata: a data frame with 16 columns ( LIB_ID, SP, WELL, SAMPLE_ID, COLLECT_YEAR, COLLECT_DATE, ... )

Final data set

Write file with filtered data set:


library(radiator)

setPop(gen) <- ~POP

# convert to tidy data set
tidy <- genomic_converter(data = gen)

tidy_gen <- tidy$tidy.data

write_delim(tidy_gen, "data/POPGEN/CLE.tidy.genotypyes", delim = "\t")

# import to convert and write out arlequin and genepop formatted files
tidy <- read_delim("data/POPGEN/CLE.tidy.genotypyes", delim = "\t")

gen <- genomic_converter(data = tidy, output = c("arlequin", "genepop", "genind"), filename = "data/POPGEN/CLE")

# create genind object
gen <- gen$genind.no.imputation

gen

Write files with individuals and Contigs still contained in data set and use to filter vcf file.

keeploci <- as.data.frame(locNames(gen)) %>%
  rename(LOCUS = `locNames(gen)`)
keeploci <- filter(loc_stats_F14, CHR %in% keeploci$LOCUS) %>%
  select(CHR, POS)
write.table(keeploci, "data/VCF/filtered.loci",
            col.names = FALSE, row.names = FALSE, quote = FALSE)
keepind <- as.data.frame(indNames(gen)) %>%
  rename(LIB_ID = `indNames(gen)`) %>%
  separate(LIB_ID, into = c("SP", "LIB", "WELL", "SAMPLE_ID"), sep = "-", extra = "merge") %>%
  separate(SAMPLE_ID, into = c("P1", "P2"), sep = "-", remove = TRUE) %>%
  unite(SAMPLE_ID, 4:5, sep = "_") %>%
  unite(LIBWELL, 2:3, sep = "-") %>%
  unite(LIB_ID, 1:3, sep = "_")
  
keepind <-filter(ind_stats_F14, INDV %in% keepind$LIB_ID) %>%
  select(INDV)
write.table(keepind, "data/VCF/filtered.ind",
            col.names = FALSE, row.names = FALSE, quote = FALSE)

Write vcf file and 012 format:


vcftools --vcf data/VCF/temp/CLE.F14.recode.vcf --out data/VCF/temp/CLE --positions data/VCF/filtered.loci --keep data/VCF/filtered.ind --recode --recode-INFO-all

vcftools --vcf data/VCF/temp/CLE.recode.vcf --out data/POPGEN/CLE --012

vcftools --vcf data/VCF/temp/CLE.recode.vcf --out data/VCF/CLE.fil --depth
vcftools --vcf data/VCF/temp/CLE.recode.vcf --out data/VCF/CLE.fil --site-mean-depth
vcftools --vcf data/VCF/temp/CLE.recode.vcf --out data/VCF/CLE.fil --missing-indv
vcftools --vcf data/VCF/temp/CLE.recode.vcf --out data/VCF/CLE.fil --missing-site
vcftools --vcf data/VCF/temp/CLE.recode.vcf --out data/VCF/CLE.fil --het
vcftools --vcf data/VCF/temp/CLE.recode.vcf --out data/VCF/CLE.fil --hardy
vcftools --vcf data/VCF/temp/CLE.recode.vcf --out data/VCF/CLE.fil --site-quality
vcftools --vcf data/VCF/temp/CLE.recode.vcf --out data/VCF/CLE.fil --geno-depth

Compare stats for final filtered data set:

# load stats files ----
ind_stats_fil <- read.ind.stats(dir = "data/VCF", vcf = "CLE.fil") %>%
  separate(INDV, into = c("SP", "LIB", "SAMPLE_ID"), sep = "_", remove = FALSE)
loc_stats_fil <- read.loc.stats(dir = "data/VCF/", vcf = "CLE.fil")
View(ind_stats_fil)
View(hap_ind_stats)
# plot missing data per indv ----
p1 <- ggplot(ind_stats_fil, aes(x = MISS_CLE.fil)) +
  geom_histogram(binwidth = .01, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MISS_CLE.fil, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0.25),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "missing data per indv") +
  theme_standard
# plot Fis per indv ----
p2 <- ggplot(ind_stats_fil, aes(x = Fis_CLE.fil)) +
  geom_histogram(binwidth = .01, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(Fis_CLE.fil, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "Fis per indv") +
  theme_standard
# plot read depth per indv ----
p3 <- ggplot(ind_stats_fil, aes(x = MEAN_DEPTH_CLE.fil)) +
  geom_histogram(binwidth = 10, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE.fil, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean read depth per indv") +
  theme_standard
# plot depth vs missing ----
p4 <- ggplot(ind_stats_fil, aes(x = MEAN_DEPTH_CLE.fil, y = MISS_CLE.fil)) +
  geom_point() +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE.fil, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(MISS_CLE.fil, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 0.25),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean depth per indv", y = "% missing data") +
  theme_standard
# plot Fis vs missing data per indv ----
p5 <- ggplot(ind_stats_fil, aes(x = Fis_CLE.fil, y = MISS_CLE.fil)) +
  geom_point() +
  geom_vline(aes(xintercept = mean(Fis_CLE.fil, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0),
                 color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(MISS_CLE.fil, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 0.25),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "Fis per indv", y = "% missing data") +
  theme_standard
# plot Fis vs mean depth per indv ----
p6 <- ggplot(ind_stats_fil, aes(x = Fis_CLE.fil, y = MEAN_DEPTH_CLE.fil)) +
  geom_point() +
  geom_vline(aes(xintercept = mean(Fis_CLE.fil, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0),
                 color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(MEAN_DEPTH_CLE.fil, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "Fis per indv", y = "mean depth per indv") +
  theme_standard
# plot distribution missing data per locus ----
p7 <- ggplot(ind_stats_fil, aes(x = MISS_CLE.fil)) +
  geom_histogram(binwidth = 0.01, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MISS_CLE.fil, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 0.1),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "% missing data per locus") +
  theme_standard
# plot distribution mean read depth ----
p8 <- ggplot(loc_stats_fil, aes(x = MEAN_DEPTH_CLE.fil)) +
  geom_histogram(binwidth = 5, color = "black", fill = "darkorange") +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE.fil, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean read depth per locus") +
  theme_standard
# plot read depth vs missing data ----
p9 <- ggplot(loc_stats_fil, aes(x = MEAN_DEPTH_CLE.fil, y = MISS_CLE.fil)) +
  geom_point() +
  geom_vline(aes(xintercept = mean(MEAN_DEPTH_CLE.fil, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(MISS_CLE.fil, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 0.1),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean depth per locus", y = "% missing data") +
  theme_standard
# plot no of SNPs per locus ----
p10 <- loc_stats_fil %>%
  count(CHR) %>%
  ggplot(aes(x = n)) +
  geom_histogram(binwidth = 1, color = "black", fill = "darkorange") + 
  labs(x = "number of SNPs per locus") +
  theme_standard
temp <- loc_stats_fil %>%
  count(CHR)
# plot number of SNPs per contig vs. mean depth ----
p11 <- left_join(temp, loc_stats_fil) %>%
  ggplot() +
  geom_point(aes(x = n, y = MEAN_DEPTH_CLE.fil)) +
  labs(x = "number of SNPs per contig", y = "mean depth") +
  theme_standard
# plot depth vs SNP quality ----
site_qual <- read.table("data/VCF/CLE.fil.lqual", 
                        header = TRUE, stringsAsFactors = FALSE) %>%
  mutate(PROB = 10^(-QUAL/10))
temp <- data.frame(loc_stats_fil$MEAN_DEPTH_CLE.fil, site_qual$QUAL) %>%
  rename(depth = loc_stats_fil.MEAN_DEPTH_CLE.fil, qual = site_qual.QUAL)
p12 <- ggplot(temp, aes(x = depth, y = qual)) +
  geom_point(size = 1) +
  geom_vline(aes(xintercept = mean(depth, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = mean(qual, na.rm = TRUE)),
                 color = "red", linetype = "dashed", size = 1) +
  geom_hline(aes(yintercept = 20),
                 color = "darkblue", linetype = "dashed", size = 1) +
  labs(x = "mean depth per locus", y = "SNP quality") +
  theme_standard
mfil <- multiplot(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, cols=2)

LS0tCnRpdGxlOiAiQnVsbHNoYXJrcyBpbiBUZXhhcyBiYXlzICYgZXN0dWFyaWVzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBoaWdobGlnaHQ6IGthdGUKICAgIHRoZW1lOiBmbGF0bHkKICAgIHRvYzogeWVzCi0tLQoKYGBge3IgbG9hZCBsaWJyYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cgpzb3VyY2UoInNjci9saWJyYXJpZXMuUiIpCnNvdXJjZSgic2NyL2dncGxvdC5SIikKc291cmNlKCJzY3IvVkNGZmlsdGVyc3RhdHMuUiIpCnNvdXJjZSgic2NyL0hhcGxvdHlwUi5SIikKc291cmNlKCJzY3IveHRyYWZ1bmN0aW9ucy5SIikKc291cmNlKCJzY3IvZ2VuaW5kLlIiKQoKYGBgCgojIFJlY2VpdmluZyBhbmQgZGVtdWx0aXBsZXhpbmcgbGlicmFyaWVzCgojIyBEZW11bHRpcGxleCBzYW1wbGVzCgpNYWtlIHN1cmUgaW5kZXggaXMgaW4gdGhlIGZpbGUgbmFtZSBvZiB0aGUgZG93bmxvYWRlZCBzZXF1ZW5jZXMgYmVmb3JlIGRlbXVsdGlwbGV4aW5nOyBpbnB1dCBmaWxlIGJhcmNvZGUgdGhlbiBpbmRleDsgYGRlbXVsdGlwbGV4LnR4dGAgaXMgaW4gVU5JWCBmb3JtYXQuCgojIyMgRGVtdWx0aXBsZXggQ0xFLTIKCkRlbXVsdGlwbGV4IHVzaW5nIG9ubHkgc2luZ2xlIGVuenltZS4KCmBgYHtiYXNoIGRlbXVsdGlwbGV4IENMRS0yLCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQoKIyBjcmVhdGUgZGVtdWx0aXBsZXhlZCBzZXF1ZW5jZSBmb2xkZXIKbWtkaXIgL2hvbWUvc29sZWFyeS9CVUxMU0hBUktTL0NMRV9QT1BHRU4vZGF0YS9TRVEvQ0xFLTIKY2QgL2hvbWUvc29sZWFyeS9CVUxMU0hBUktTL0NMRV9QT1BHRU4vZGF0YS9TRVEvQ0xFLTIKCiMgZGVtdWx0aXBsZXggZmlsZXMKZGVtdWx0aXBsZXgucGwgLWkgRGVtdWx0aXBsZXhfQ0xFLTIudHh0IC1vIEV4dHJhY3RfQ0xFLTIuc2ggLWUxIHNwaEkgLXAgL2hvbWUvc29sZWFyeS9CVUxMU0hBUktTL0NMRV9QT1BHRU4vZGF0YS9TRVEvQ0xFLTIgLWQgL2hvbWUvREFUQS9CVUxMU0hBUktTL0NsZS0yCmNobW9kIDc1NSBFeHRyYWN0X0NMRS0yLnNoCi4vRXh0cmFjdF9DTEUtMi5zaAoKIyBkZWxldGUgdW5uZWNlc3NhcnkgZmlsZXMgZ2VuZXJhdGVkIGR1cmluZyBkZW11bHRpcGxleGluZwpybSBzYW1wbGUqCgpgYGAKCiMjIyBEZW11bHRpcGxleCBDTEUtMwoKRGVtdWx0aXBsZXggdXNpbmcgb25seSBzaW5nbGUgZW56eW1lLgoKYGBge2Jhc2ggZGVtdWx0aXBsZXggQ0xFLTMsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CgojIGNyZWF0ZSBkZW11bHRpcGxleGVkIHNlcXVlbmNlIGZvbGRlcgpta2RpciAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL1NFUS9DTEUtMwpjZCAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL1NFUS9DTEUtMwoKIyBkZW11bHRpcGxleCBmaWxlcwpkZW11bHRpcGxleC5wbCAtaSBEZW11bHRpcGxleF9DTEUtMy50eHQgLW8gRXh0cmFjdF9DTEUtMy5zaCAtZTEgc3BoSSAtcCAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL1NFUS9DTEUtMyAtZCAvaG9tZS9EQVRBL0JVTExTSEFSS1MvQ2xlLTMKY2htb2QgNzU1IEV4dHJhY3RfQ0xFLTMuc2gKLi9FeHRyYWN0X0NMRS0zLnNoCgojIGRlbGV0ZSB1bm5lY2Vzc2FyeSBmaWxlcyBnZW5lcmF0ZWQgZHVyaW5nIGRlbXVsdGlwbGV4aW5nCnJtIHNhbXBsZSoKCmBgYAoKIyMgUXVhbGl0eSBjb250cm9sIGRlbXVsdGlwbGV4ZWQgcmVhZHMKClBhcnNlIHByb2Nlc3MgcmFkdGFncyBsb2cgZmlsZS4KCmBgYHtyIHBhcnNlIHByb2Nlc3MgcmFkdGFncywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiMgaWYgYWxyZWFkeSBydW4gdW5hdHRhY2ggbGlzdApybShsKQoKIyBjcmVhdGUgZW1wdHkgbGlzdApsIDwtIGxpc3QoKQoKIyBDTEUtMiAtLS0tCgojIG51bWJlciBvZiBzYW1wbGVzIGluIGluZGV4CmluZGV4IDwtICJDR0FUR1QiCmxpYiA8LSAiQ0xFMiIKbl9zYW1wbGVzIDwtIDI5CgojIHJlYWQgaW4gc3VtbWFyeSBwZXIgYmFyY29kZQpsW1siQ0xFMi0yIl1dIDwtIHJlYWRfdGFibGUyKCJkYXRhL1NFUS9DTEUyX0NHQVRHVF9yYWR0YWdzLmxvZyIsCiAgICAgICAgICAgIHNraXAgPSAxMywgbl9tYXggPSBuX3NhbXBsZXMsCiAgICAgICAgICAgIGNvbF9uYW1lcyA9IGMoIkJBUkNPREUiLCAiVE9UQUxfUkVBRFMiLCAiQU1CSUdfUkVBRFMiLCAiTFFfUkVBRFMiLCAiUkVUQUlORUQiKSkgJT4lCiAgbXV0YXRlKFBST1BfUkVUQUlORUQgPSBSRVRBSU5FRC9UT1RBTF9SRUFEUywKICAgICAgICAgSU5ERVggPSBpbmRleCwKICAgICAgICAgTElCUkFSWSA9IGxpYikgJT4lCiAgc2VsZWN0KExJQlJBUlksIElOREVYLCBCQVJDT0RFLCBQUk9QX1JFVEFJTkVELCBUT1RBTF9SRUFEUywgUkVUQUlORUQsIEFNQklHX1JFQURTLCBMUV9SRUFEUykKCiMgbnVtYmVyIG9mIHNhbXBsZXMgaW4gaW5kZXgKaW5kZXggPC0gIlRHQUNDQSIKbGliIDwtICJDTEUyIgpuX3NhbXBsZXMgPC0gMjkKCiMgcmVhZCBpbiBzdW1tYXJ5IHBlciBiYXJjb2RlCmxbWyJDTEUyLTQiXV0gPC0gcmVhZF90YWJsZTIoImRhdGEvU0VRL0NMRTJfVEdBQ0NBX3JhZHRhZ3MubG9nIiwKICAgICAgICAgICAgc2tpcCA9IDEzLCBuX21heCA9IG5fc2FtcGxlcywKICAgICAgICAgICAgY29sX25hbWVzID0gYygiQkFSQ09ERSIsICJUT1RBTF9SRUFEUyIsICJBTUJJR19SRUFEUyIsICJMUV9SRUFEUyIsICJSRVRBSU5FRCIpKSAlPiUKICBtdXRhdGUoUFJPUF9SRVRBSU5FRCA9IFJFVEFJTkVEL1RPVEFMX1JFQURTLAogICAgICAgICBJTkRFWCA9IGluZGV4LAogICAgICAgICBMSUJSQVJZID0gbGliKSAlPiUKICBzZWxlY3QoTElCUkFSWSwgSU5ERVgsIEJBUkNPREUsIFBST1BfUkVUQUlORUQsIFRPVEFMX1JFQURTLCBSRVRBSU5FRCwgQU1CSUdfUkVBRFMsIExRX1JFQURTKQoKIyBudW1iZXIgb2Ygc2FtcGxlcyBpbiBpbmRleAppbmRleCA8LSAiQ0FHQVRDIgpsaWIgPC0gIkNMRTIiCm5fc2FtcGxlcyA8LSAyOQoKIyByZWFkIGluIHN1bW1hcnkgcGVyIGJhcmNvZGUKbFtbIkNMRTItNyJdXSA8LSByZWFkX3RhYmxlMigiZGF0YS9TRVEvQ0xFMl9DQUdBVENfcmFkdGFncy5sb2ciLAogICAgICAgICAgICBza2lwID0gMTMsIG5fbWF4ID0gbl9zYW1wbGVzLAogICAgICAgICAgICBjb2xfbmFtZXMgPSBjKCJCQVJDT0RFIiwgIlRPVEFMX1JFQURTIiwgIkFNQklHX1JFQURTIiwgIkxRX1JFQURTIiwgIlJFVEFJTkVEIikpICU+JQogIG11dGF0ZShQUk9QX1JFVEFJTkVEID0gUkVUQUlORUQvVE9UQUxfUkVBRFMsCiAgICAgICAgIElOREVYID0gaW5kZXgsCiAgICAgICAgIExJQlJBUlkgPSBsaWIpICU+JQogIHNlbGVjdChMSUJSQVJZLCBJTkRFWCwgQkFSQ09ERSwgUFJPUF9SRVRBSU5FRCwgVE9UQUxfUkVBRFMsIFJFVEFJTkVELCBBTUJJR19SRUFEUywgTFFfUkVBRFMpCgojIG51bWJlciBvZiBzYW1wbGVzIGluIGluZGV4CmluZGV4IDwtICJUQUdDVFQiCmxpYiA8LSAiQ0xFMiIKbl9zYW1wbGVzIDwtIDI5CgojIHJlYWQgaW4gc3VtbWFyeSBwZXIgYmFyY29kZQpsW1siQ0xFMi0xMCJdXSA8LSByZWFkX3RhYmxlMigiZGF0YS9TRVEvQ0xFMl9UQUdDVFRfcmFkdGFncy5sb2ciLAogICAgICAgICAgICBza2lwID0gMTMsIG5fbWF4ID0gbl9zYW1wbGVzLAogICAgICAgICAgICBjb2xfbmFtZXMgPSBjKCJCQVJDT0RFIiwgIlRPVEFMX1JFQURTIiwgIkFNQklHX1JFQURTIiwgIkxRX1JFQURTIiwgIlJFVEFJTkVEIikpICU+JQogIG11dGF0ZShQUk9QX1JFVEFJTkVEID0gUkVUQUlORUQvVE9UQUxfUkVBRFMsCiAgICAgICAgIElOREVYID0gaW5kZXgsCiAgICAgICAgIExJQlJBUlkgPSBsaWIpICU+JQogIHNlbGVjdChMSUJSQVJZLCBJTkRFWCwgQkFSQ09ERSwgUFJPUF9SRVRBSU5FRCwgVE9UQUxfUkVBRFMsIFJFVEFJTkVELCBBTUJJR19SRUFEUywgTFFfUkVBRFMpCgojIENMRS0zIC0tLS0KCiMgbnVtYmVyIG9mIHNhbXBsZXMgaW4gaW5kZXgKaW5kZXggPC0gIkNHQVRHVCIKbGliIDwtICJDTEUyIgpuX3NhbXBsZXMgPC0gMjkKCiMgcmVhZCBpbiBzdW1tYXJ5IHBlciBiYXJjb2RlCmxbWyJDTEUzLTIiXV0gPC0gcmVhZF90YWJsZTIoImRhdGEvU0VRL0NMRTNfQ0dBVEdUX3JhZHRhZ3MubG9nIiwKICAgICAgICAgICAgc2tpcCA9IDEzLCBuX21heCA9IG5fc2FtcGxlcywKICAgICAgICAgICAgY29sX25hbWVzID0gYygiQkFSQ09ERSIsICJUT1RBTF9SRUFEUyIsICJBTUJJR19SRUFEUyIsICJMUV9SRUFEUyIsICJSRVRBSU5FRCIpKSAlPiUKICBtdXRhdGUoUFJPUF9SRVRBSU5FRCA9IFJFVEFJTkVEL1RPVEFMX1JFQURTLAogICAgICAgICBJTkRFWCA9IGluZGV4LAogICAgICAgICBMSUJSQVJZID0gbGliKSAlPiUKICBzZWxlY3QoTElCUkFSWSwgSU5ERVgsIEJBUkNPREUsIFBST1BfUkVUQUlORUQsIFRPVEFMX1JFQURTLCBSRVRBSU5FRCwgQU1CSUdfUkVBRFMsIExRX1JFQURTKQoKCiMgbnVtYmVyIG9mIHNhbXBsZXMgaW4gaW5kZXgKaW5kZXggPC0gIlRHQUNDQSIKbGliIDwtICJDTEUzIgpuX3NhbXBsZXMgPC0gMjkKCiMgcmVhZCBpbiBzdW1tYXJ5IHBlciBiYXJjb2RlCmxbWyJDTEUzLTQiXV0gPC0gcmVhZF90YWJsZTIoImRhdGEvU0VRL0NMRTNfVEdBQ0NBX3JhZHRhZ3MubG9nIiwKICAgICAgICAgICAgc2tpcCA9IDEzLCBuX21heCA9IG5fc2FtcGxlcywKICAgICAgICAgICAgY29sX25hbWVzID0gYygiQkFSQ09ERSIsICJUT1RBTF9SRUFEUyIsICJBTUJJR19SRUFEUyIsICJMUV9SRUFEUyIsICJSRVRBSU5FRCIpKSAlPiUKICBtdXRhdGUoUFJPUF9SRVRBSU5FRCA9IFJFVEFJTkVEL1RPVEFMX1JFQURTLAogICAgICAgICBJTkRFWCA9IGluZGV4LAogICAgICAgICBMSUJSQVJZID0gbGliKSAlPiUKICBzZWxlY3QoTElCUkFSWSwgSU5ERVgsIEJBUkNPREUsIFBST1BfUkVUQUlORUQsIFRPVEFMX1JFQURTLCBSRVRBSU5FRCwgQU1CSUdfUkVBRFMsIExRX1JFQURTKQoKCiMgbnVtYmVyIG9mIHNhbXBsZXMgaW4gaW5kZXgKaW5kZXggPC0gIkNBR0FUQyIKbGliIDwtICJDTEUzIgpuX3NhbXBsZXMgPC0gMjkKCiMgcmVhZCBpbiBzdW1tYXJ5IHBlciBiYXJjb2RlCmxbWyJDTEUzLTciXV0gPC0gcmVhZF90YWJsZTIoImRhdGEvU0VRL0NMRTNfQ0FHQVRDX3JhZHRhZ3MubG9nIiwKICAgICAgICAgICAgc2tpcCA9IDEzLCBuX21heCA9IG5fc2FtcGxlcywKICAgICAgICAgICAgY29sX25hbWVzID0gYygiQkFSQ09ERSIsICJUT1RBTF9SRUFEUyIsICJBTUJJR19SRUFEUyIsICJMUV9SRUFEUyIsICJSRVRBSU5FRCIpKSAlPiUKICBtdXRhdGUoUFJPUF9SRVRBSU5FRCA9IFJFVEFJTkVEL1RPVEFMX1JFQURTLAogICAgICAgICBJTkRFWCA9IGluZGV4LAogICAgICAgICBMSUJSQVJZID0gbGliKSAlPiUKICBzZWxlY3QoTElCUkFSWSwgSU5ERVgsIEJBUkNPREUsIFBST1BfUkVUQUlORUQsIFRPVEFMX1JFQURTLCBSRVRBSU5FRCwgQU1CSUdfUkVBRFMsIExRX1JFQURTKQoKIyBudW1iZXIgb2Ygc2FtcGxlcyBpbiBpbmRleAppbmRleCA8LSAiVEFHQ1RUIgpsaWIgPC0gIkNMRTMiCm5fc2FtcGxlcyA8LSAyOQoKIyByZWFkIGluIHN1bW1hcnkgcGVyIGJhcmNvZGUKbFtbIkNMRTMtMTAiXV0gPC0gcmVhZF90YWJsZTIoImRhdGEvU0VRL0NMRTNfVEFHQ1RUX3JhZHRhZ3MubG9nIiwKICAgICAgICAgICAgc2tpcCA9IDEzLCBuX21heCA9IG5fc2FtcGxlcywKICAgICAgICAgICAgY29sX25hbWVzID0gYygiQkFSQ09ERSIsICJUT1RBTF9SRUFEUyIsICJBTUJJR19SRUFEUyIsICJMUV9SRUFEUyIsICJSRVRBSU5FRCIpKSAlPiUKICBtdXRhdGUoUFJPUF9SRVRBSU5FRCA9IFJFVEFJTkVEL1RPVEFMX1JFQURTLAogICAgICAgICBJTkRFWCA9IGluZGV4LAogICAgICAgICBMSUJSQVJZID0gbGliKSAlPiUKICBzZWxlY3QoTElCUkFSWSwgSU5ERVgsIEJBUkNPREUsIFBST1BfUkVUQUlORUQsIFRPVEFMX1JFQURTLCBSRVRBSU5FRCwgQU1CSUdfUkVBRFMsIExRX1JFQURTKQoKIyBjcmVhdGUgc2luZ2xlIGRhdGEgZnJhbWUKcmFkdGFnc2xvZyA8LSBsZHBseShsLCBkYXRhLmZyYW1lKSAlPiUKICBzZWxlY3QoLWAuaWRgLCBMUV9SRUFEUykgJT4lCiAgdW5pdGUoTElCX0lEWCwgTElCUkFSWSwgSU5ERVgsIHNlcCA9ICJfIiwgcmVtb3ZlID0gRkFMU0UpCgp3cml0ZV9kZWxpbShyYWR0YWdzbG9nLCAicmVzdWx0cy9hbGwucmFkdGFncy5sb2ciKQoKYGBgCgpDb21wYXJlIGRlbXVsdGlwbGV4ZWQgcmVhZHMgcGVyIGxpYnJhcnkgJiBpbmRleC4KClBsb3QgZGlzdHJpYnV0aW9ucyBvZiB0b3RhbCBhbmQgcHJvcG9ydGlvbiBvZiByZXRhaW5lZCByZWFkcy4KCmBgYHtyIHBsb3QgcmVhZHMsIGZpZy5oZWlnaHQ9MTgsIGZpZy53aWR0aD02LCBtZXNzYWdlPVRSVUUsIHdhcm5pbmc9VFJVRX0KCnJhZHRhZ3Nsb2cgJT4lCiAgc2VsZWN0KExJQl9JRFgsIExJQlJBUlksIElOREVYLCBCQVJDT0RFLCBQUk9QX1JFVEFJTkVELCBUT1RBTF9SRUFEUykgJT4lCiAgZ2F0aGVyKGtleSA9IFNUQVQsIHZhbHVlID0gUkVBRFMsIDU6NikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gUkVBRFMpKSArCiAgZ2VvbV9oaXN0b2dyYW0oY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgbGFicyh4ID0gInJlYWRzIikgKwogIGZhY2V0X2dyaWQoTElCX0lEWCB+IFNUQVQsIHNjYWxlcyA9ICJmcmVlIikgKwogIHRoZW1lX3N0YW5kYXJkCgpgYGAKCiMgUmVmZXJlbmNlIGNvbnN0cnVjdGlvbgoKVmFsdWVzIGNob3NlbiBmb3IgTWlTZXEgUmVmZXJlbmNlOgoKKiAqKmMqKiA9IDAuOAoqICoqSzEqKiA9IDUKKiAqKksyKiogPSA2CgojIFJlYWQgbWFwcGluZwoKIyMgTWFwIHJlYWRzIHVzaW5nIEJXQSAoaW4gKipkRG9jZW50KiogcGlwZWxpbmUpCgpUcmFuc2ZlciBjb3B5IG9mIGByZWZlcmVuY2UuZmFzdGFgIGFuZCBhc3NvY2lhdGVkIGZpbGVzIGZvciBLMSA9IDUgYW5kIEsyID0gNiBpbnRvIGVhY2ggZGlyZWN0b3J5IGNvbnRhaW5pbmcgZGVtdWx0aXBsZXhlZCBhbmQgcXVhbGl0eSB0cmltbWVkIHNlcXVlbmNlcwoKQW55IHJlbmFtaW5nIG9mIGZpbGVzIG5lZWRzIHRvIGhhcHBlbiBiZWZvcmUgYGZhc3RxYC1maWxlcyBhcmUgbWFwcGVkLiBUaGUgcG9wdWxhdGlvbiBkZXNpZ25hdGlvbiAoYmVmb3JlIHVuZGVyc2NvcmUpIGlzIHVzZWQgYnkgYEZyZWVCYXllc2AgdG8gY2FsbCBTTlBzIHNvIGl0IGlzIGltcG9ydGFudCB0aGF0IHBvcHVsYXRpb24gZGVzaWduYXRpb24gbWFrZSBiaW9sb2dpY2FsIHNlbnNlLiBBbGwgZmlsZXMgc2hvdWxkIGJlIG5hbWVkIGBQT1BfUExBVEUtV0VMTF9TQU1QTEVOQU1FU2AuCgpSdW4gYGREb2NlbnRgIGZyb20gd2l0aGluIGVhY2ggTGlicmFyeSBkaXJlY3RvcnkgdG8gbWFwIHJlYWRzIHRvIGByZWZlcmVuY2UuZmFzdGFgLgoKYGBge2Jhc2ggcnVuIGREb2NlbnQsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CgpjZCAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL1NFUS9DTEUtMgpkRG9jZW50CgpjZCAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL1NFUS9DTEUtMwpkRG9jZW50CgpgYGAKCiMjIFFBL1FDIHJlYWQgbWFwcGluZwoKIyMjIFF1ZXJ5IG1hcHBpbmcgc3RhdGlzdGljcwoKRHVyaW5nIHRoZSBtYXBwaW5nIHN0YWdlLCBgZERvY2VudGAgY2FsbHMgYEJXQWAgdG8gbWFwIHJlYWRzIGZyb20gdGhlIGluZGl2aWR1YWxzIGluIHRoZSBmb2xkZXIgdG8gdGhlIGdlbmVyYXRlZCBNaVNlcVJlZmVyZW5jZSBhbmQgY3JlYXRlIGEgYC1SRy5iYW1gLWZpbGUgZm9yIGVhY2ggaW5kaXZpZHVhbC4gVGhlIHNlY29uZCBjb2x1bW4gb2YgYSBCQU0gKG9yIFNBTSkgZmlsZSBjb250YWlucyBGTEFHcyB3aXRoIGJpbmFyeSBlbmNvZGVkIGluZm9ybWF0aW9uIG9uIG1hcHBpbmcsIHBhaXJlZG5lc3MgZXRjLiB0aGF0IGNhbiBiZSB1c2VkIHRvIGNvbXBhcmUgdGhlIG1hcHBpbmcgZWZmaWNpZW5jeSBvZiB0aGUgZ2VuZXJhdGVkIE1pU2VxIHJlZmVyZW5jZXMuCgpDb3VudCBudW1iZXIgb2YgcmVhZHMgYW5kIG1hcHBlZCByZWFkcyB1c2luZyBgc2FtdG9vbHMgaWR4c3RhdHMgPGFsbi1SRy5iYW0+YCB3aGljaCB3aWxsIHJldHJpZXZlIGFuZCBwcmludCBzdGF0cyBpbiB0aGUgYmFtLWZpbGUuIFRoZSBvdXRwdXQgaXMgVEFCLWRlbGltaXRlZCB3aXRoIGVhY2ggbGluZSBjb25zaXN0aW5nIG9mIHJlZmVyZW5jZSBzZXF1ZW5jZSBuYW1lLCBzZXF1ZW5jZSBsZW5ndGgsICMgbWFwcGVkIHJlYWRzIGFuZCAjIHVubWFwcGVkIChlbXB0eSkgcmVhZHMuIGBzYW10b29sc2AgY2FuIGFsc28gYmUgYmUgdXNlZCB0byBxdWVyeSBgc2FtdG9vbHMgZmxhZ3N0YXQgZmlsZS5iYW1gIHdoaWNoIHJldHVybnMgYW4gb3V0cHV0IGNvbnRhaW5pbmcgdGhlIG51bWJlciBvZiByZWFkcyBmb3Igd2hpY2ggZWFjaCBmbGFnIGlzIHRydWUuCgojIyMjIFJ1biBGbGFnc3RhdHMKCmREb2NlbnQgd3JpdGVzIG91dCBhIGZpbGUgY2FsbGVkIGBiYW1saXN0Lmxpc3RgIHRoYXQgY29udGFpbnMgYWxsIHRoZSBiYW0gZmlsZXMgdGhhdCB3ZXJlIGdlbmVyYXRlZCBkdXJpbmcgcmVhZCBtYXBwaW5nIGluIGBkRG9jZW50YCB1c2luZyBgQldBYC4gV3JpdGUgc2NyaXB0IHRvIGdhdGhlciBmbGFnc3RhdHMgZnJvbSBhbGwgYGJhbWAtZmlsZXMuCgpgYGB7ciBmbGFnc3RhdHMgc2NyaXB0fQoKIyB3cml0ZSBzY3JpcHQgdG8gZ2F0aGVyIGZsYWdzdGF0cwpsIDwtIGxpc3QoKQoKbFtbIkNMRTIiXV0gPC0gcmVhZF90YWJsZTIoImRhdGEvU0VRL0NMRS0yL2JhbWxpc3QubGlzdCIsIGNvbF9uYW1lcyA9ICJCQU0iKSAlPiUKICBtdXRhdGUoUEFUSCA9ICJkYXRhL1NFUS9DTEUtMi8iLAogICAgICAgICBDT01NQU5EID0gInNhbXRvb2xzIGZsYWdzdGF0IiwKICAgICAgICAgT1VUID0gIj4+IGRhdGEvU0VRL0NMRTIuZmxhZ3N0YXRzIikgJT4lCiAgc2VsZWN0KCBDT01NQU5ELCBQQVRILCBCQU0sIE9VVCkgJT4lCiAgdW5pdGUoRklMRSwgMjozLCBzZXAgPSAiIikKCmxbWyJDTEUzIl1dIDwtIHJlYWRfdGFibGUyKCJkYXRhL1NFUS9DTEUtMy9iYW1saXN0Lmxpc3QiLCBjb2xfbmFtZXMgPSAiQkFNIikgJT4lCiAgbXV0YXRlKFBBVEggPSAiZGF0YS9TRVEvQ0xFLTMvIiwKICAgICAgICAgQ09NTUFORCA9ICJzYW10b29scyBmbGFnc3RhdCIsCiAgICAgICAgIE9VVCA9ICI+PiBkYXRhL1NFUS9DTEUzLmZsYWdzdGF0cyIpICU+JQogIHNlbGVjdCggQ09NTUFORCwgUEFUSCwgQkFNLCBPVVQpICU+JQogIHVuaXRlKEZJTEUsIDI6Mywgc2VwID0gIiIpCgpiYW0gPC0gbGRwbHkobCwgZGF0YS5mcmFtZSkgJT4lCiAgc2VsZWN0KC1gLmlkYCkKCndyaXRlX2RlbGltKGJhbSwgInNjci9mbGFnc3RhdHMuc2giLCBkZWxpbSA9ICJcdCIsIGNvbF9uYW1lcyA9IEZBTFNFKQoKYGBgCgpSdW4gZmxhZ3N0YXRzLgoKYGBge2Jhc2ggcnVuIGZsYWdzdGF0cywgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KCmNobW9kIDc1NSBzY3IvZmxhZ3N0YXRzLnNoCi4vc2NyL2ZsYWdzdGF0cy5zaAoKYGBgCgojIyMjIFJ1biBpZHhzdGF0cwoKV3JpdGUgc2NyaXB0IHRvIGdhdGhlciBpZHhzdGF0cyBmcm9tIGFsbCBgYmFtYC1maWxlcy4KCmBgYHtyIHNjcmlwdCBpZHhzdGF0c30KCiMgd3JpdGUgc2NyaXB0IHRvIGdhdGhlciBpZHhzdGF0cwpsIDwtIGxpc3QoKQoKbFtbIkNMRTIiXV0gPC0gcmVhZF90YWJsZTIoImRhdGEvU0VRL0NMRS0yL2JhbWxpc3QubGlzdCIsIGNvbF9uYW1lcyA9ICJCQU0iKSAlPiUKICBtdXRhdGUoUEFUSCA9ICJkYXRhL1NFUS9DTEUtMi8iLAogICAgICAgICBDT01NQU5EID0gInNhbXRvb2xzIGlkeHN0YXRzIiwKICAgICAgICAgT1VUID0gIj4+IGRhdGEvU0VRL0NMRTIuaWR4c3RhdHMiKSAlPiUKICBzZWxlY3QoIENPTU1BTkQsIFBBVEgsIEJBTSwgT1VUKSAlPiUKICB1bml0ZShGSUxFLCAyOjMsIHNlcCA9ICIiKQoKbFtbIkNMRTMiXV0gPC0gcmVhZF90YWJsZTIoImRhdGEvU0VRL0NMRS0zL2JhbWxpc3QubGlzdCIsIGNvbF9uYW1lcyA9ICJCQU0iKSAlPiUKICBtdXRhdGUoUEFUSCA9ICJkYXRhL1NFUS9DTEUtMy8iLAogICAgICAgICBDT01NQU5EID0gInNhbXRvb2xzIGlkeHN0YXRzIiwKICAgICAgICAgT1VUID0gIj4+IGRhdGEvU0VRL0NMRTMuaWR4c3RhdHMiKSAlPiUKICBzZWxlY3QoIENPTU1BTkQsIFBBVEgsIEJBTSwgT1VUKSAlPiUKICB1bml0ZShGSUxFLCAyOjMsIHNlcCA9ICIiKQoKYmFtIDwtIGxkcGx5KGwsIGRhdGEuZnJhbWUpICU+JQogIHNlbGVjdCgtYC5pZGApCgp3cml0ZV9kZWxpbShiYW0sICJzY3IvaWR4c3RhdHMuc2giLCBkZWxpbSA9ICJcdCIsIGNvbF9uYW1lcyA9IEZBTFNFKQoKYGBgCgpSdW4gaW5keHN0YXRzLgoKYGBge2Jhc2ggcnVuIGlkeHN0YXRzLCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQoKY2htb2QgNzU1IHNjci9pZHhzdGF0cy5zaAouL3Njci9pZHhzdGF0cy5zaAoKYGBgCgojIyMgRm9ybWF0IHN0YXRzIGZpbGVzIGludG8gdGlkeSBkYXRhIHNldHMKCkFwcGVuZGluZyB0aGUgZmlsZSByZXN1bHRzIGluIHRoZSBpbmZvcm1hdGlvbiBwZXIgaW5kaXZpZHVhbCBiZWluZyBwcmludGVkIGluIGEgbmV3IHNldCBvZiByb3cgYmVpbmcgYXBwZW5kZWQgdG8gdGhlIGZpbGUsIGkuZS4gdGhlcmUgd2lsbCBiZSBhcyBtYW55IHJvd3MgZm9yIGEgZ2l2ZW4gbG9jdXMgYXMgaW5kaXZpZHVhbHMgd2VyZSBtYXBwZWQuIFRoZSBmaWxlIGNhbiBiZSByZS1mb3JtYXR0ZWQgYW5kIHN1bW1hcnkgc3RhdGlzdGljcyBjYWxjdWxhdGVkIHVzaW5nIGRwbHlyIGFuZCB0aWR5ci4KCkZvcm1hdCBpZHhzdGF0czoKCmBgYHtyIGZvcm1hdCBpZHhzdGF0c30KCiMgY3JlYXRlIHZlY3RvcnMgb2YgZmlsZXMgdG8gYmUgaW1wb3J0ZWQsIHJlZmVyZW5jZSBjb2RlcywgSzEgYW5kIEsyLCBkYXRhZnJhbWUgbmFtZXMKZmlsZW5hbWVzIDwtIGxpc3QuZmlsZXMocGF0aCA9ICJkYXRhL1NFUSIsIHBhdHRlcm4gPSAiKi5pZHhzdGF0cyIpCm5hbWVzIDwtIHN1YnN0cihmaWxlbmFtZXMsIDEsIDgpCmxpYiA8LSBzdWJzdHIoZmlsZW5hbWVzLCAxLCA0KQoKIyBpbXBvcnQgZGF0YQpmb3IgKGkgaW4gbmFtZXMpewogIGZpbGVwYXRoIDwtIGZpbGUucGF0aCgiZGF0YS9TRVEiLCBwYXN0ZShpLCAnc3RhdHMnLCBzZXAgPSIiKSkKICBhc3NpZ24oaSwgcmVhZC50YWJsZShmaWxlcGF0aCwgc2VwID0gIiIsIGhlYWRlciA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICBjb2wubmFtZXMgPSBjKCJMb2N1cyIsICJMZW5ndGgiLCAiUmVhZHNfTWFwcGVkIiwgJ2JsYW5rJykpICU+JQogICAgICAgICAgIHNlbGVjdCgxOjMpKQogIH0KCiMgIyBtYWtlIHN1cmUgdG8gZGVsZXRlIG9sZCBsaXN0IGlmIHJlcnVubmluZyB0aGUgY29kZQojIHJtKGRmbGlzdF9pZHgpCiMgcm0oTWFwU3RhdHMuaWR4KQoKIyBDcmVhdGUgbGlzdCBvZiBvbmUgZGF0YWZyYW1lIHBlciBpZHhzdGF0cyBmaWxlIGFuZCBncm91cCBieSBsb2N1cwpkZmxpc3RfaWR4IDwtIGxhcHBseShscyhwYXR0ZXJuID0gIiouaWR4IiksIGdldCkKCmZvciAoZGYgaW4gMTpsZW5ndGgoZGZsaXN0X2lkeCkpewogIHggPC0gZGZsaXN0X2lkeFtbZGZdXQogIHhbWydMb2N1cyddXSA8LSBhcy5jaGFyYWN0ZXIoeFtbJ0xvY3VzJ11dKQogIHggPSB4ICU+JSBncm91cF9ieShMb2N1cykKICBkZmxpc3RfaWR4W1tkZl1dIDwtIHgKfQoKIyBDcmVhdGUgbmV3IGRhdGFmcmFtZXMgd2l0aCBzdW1tYXJ5IHN0YXRzIHBlciBsaWJyYXJ5IGFuZCBiaW5kIGludG8gZmluYWwgb3V0cHV0L2RhdGFmcmFtZQpNYXBTdGF0cy5pZHggPC0gZGF0YS5mcmFtZSgpCgpmb3IgKGRmIGluIDE6bGVuZ3RoKGRmbGlzdF9pZHgpKXsKICAgIAogIHggPSBzdW1tYXJpemUoZGZsaXN0X2lkeFtbZGZdXSwKICAgICAgICAgICAgICAgIExlbmd0aCA9IG1lYW4oTGVuZ3RoKSwKICAgICAgICAgICAgICAgIE1lYW5fTWFwcGVkID0gbWVhbihSZWFkc19NYXBwZWQpLAogICAgICAgICAgICAgICAgU3VtX01hcHBlZCA9IHN1bShSZWFkc19NYXBwZWQpLAogICAgICAgICAgICAgICAgTWluX01hcHBlZCA9IG1pbihSZWFkc19NYXBwZWQpLAogICAgICAgICAgICAgICAgTWF4X01hcHBlZCA9IG1heChSZWFkc19NYXBwZWQpLAogICAgICAgICAgICAgICAgU0RfTWFwcGVkID0gc2QoUmVhZHNfTWFwcGVkKSkKICB4W3ggPT0gMF0gPC0gTkEKCiAgdGVtcCA8LSBzdW1tYXJpemUoeCwgTWVhbl9NYXBwZWRfTm9uMCA9IG1lYW4oTWVhbl9NYXBwZWQsIG5hLnJtID0gVFJVRSkpICU+JQogICAgbXV0YXRlKExpYiA9IGxpYltkZl0sCiAgICAgICAgICAgTm90X01hcHBlZCA9IG5yb3coZmlsdGVyKHgsIGlzLm5hKFN1bV9NYXBwZWQpKSksCiAgICAgICAgICAgTl9Mb2NpX1JlZiA9IG5yb3coeCkpICU+JQogICAgc2VsZWN0KExpYiwgTl9Mb2NpX1JlZiwgTm90X01hcHBlZCwgTWVhbl9NYXBwZWRfTm9uMCkKCiAgTWFwU3RhdHMuaWR4IDwtIGJpbmRfcm93cyhNYXBTdGF0cy5pZHgsIHRlbXApCn0KCk1hcFN0YXRzLmlkeCA8LSBNYXBTdGF0cy5pZHggJT4lCiAgbXV0YXRlKFBST1BfRU1QVFkgPSByb3VuZChOb3RfTWFwcGVkL05fTG9jaV9SZWYqMTAwLCBkaWdpdHMgPSAyKSwKICAgICAgICAgQ09OVElHU19NQVBQRUQgPSBOX0xvY2lfUmVmIC0gTm90X01hcHBlZCkKCndyaXRlLnRhYmxlKE1hcFN0YXRzLmlkeCwgInJlc3VsdHMvTWFwU3RhdHMuaWR4IiwgcXVvdGUgPSBGQUxTRSkKCiMgcmVtb3ZlIGxhcmdlIChkdXBsaWNhdGUpIGZpbGVzCnJtKENMRTIuaWR4KQpybShDTEUzLmlkeCkKCk1hcFN0YXRzLmlkeAoKYGBgCgpGb3JtYXQgZmxhZ3N0YXRzCgpgYGB7ciBmb3JtYXQgZmxhZ3N0YXRzfQoKIyBGaWxlcyB0byBiZSBpbXBvcnRlZApmaWxlbmFtZXMgPC0gbGlzdC5maWxlcyhwYXRoPSdkYXRhL1NFUScsIHBhdHRlcm4gPSAnKi5mbGFnc3RhdHMnKQoKIyBjcmVhdGUgdmVjdG9ycyBvZiBmaWxlcyB0byBiZSBpbXBvcnRlZApuYW1lcyA8LSBzdWJzdHIoZmlsZW5hbWVzLCAxLCA5KQpsaWIgPC0gc3Vic3RyKGZpbGVuYW1lcywgMSwgNCkKCiMgaW1wb3J0IGRhdGEKZm9yIChpIGluIG5hbWVzKXsKICBmaWxlcGF0aCA8LSBmaWxlLnBhdGgoJ2RhdGEvU0VRJywgcGFzdGUoaSwgJ3N0YXRzJywgc2VwID0iIikpCiAgYXNzaWduKGksIHJlYWQuY3N2KGZpbGVwYXRoLCBzZXAgPSAiKyIsIGhlYWRlciA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICBjb2wubmFtZXMgPSBjKCJOX1JlYWRzIiwgIkNBVCIpLAogICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogICAgICAgICAgIHNlbGVjdCgxOjIpKQp9CgojIENyZWF0ZSBsaXN0IG9mIG9uZSBkYXRhZnJhbWUgcGVyIGZsYWdzdGF0cyBmaWxlIGFuZCBjcmVhdGUgdGlkeSBkYXRhIHNldAojIHNob3VsZCBiZSAzIGVsZW1lbnRzL2xpYnJhcmllcwpybShkZmxpc3RfZmxhZykKCmRmbGlzdF9mbGFnIDwtIGxhcHBseShscyhwYXR0ZXJuID0gIipmbGFnIiksIGdldCkKCiMgQ2hhbmdlIE5fUmVhZHMgdG8gbnVtZXJpYwpmb3IgKGRmIGluIDE6bGVuZ3RoKGRmbGlzdF9mbGFnKSl7CiAgeCA8LSBkZmxpc3RfZmxhZ1tbZGZdXQogIHhbWydOX1JlYWRzJ11dIDwtIGFzLm51bWVyaWMoeFtbJ05fUmVhZHMnXV0pCiAgZGZsaXN0X2ZsYWdbW2RmXV0gPC0geAp9Cgpmb3IgKGRmIGluIDE6bGVuZ3RoKGRmbGlzdF9mbGFnKSl7CiAgeCA8LSBkZmxpc3RfZmxhZ1tbZGZdXQogIAogIG4gPC0gbnJvdyh4KS8xNAoKICB4IDwtIHggJT4lCiAgICBmaWx0ZXIoZ3JlcGwoIjAgbWFwcGVkfHByb3Blcmx5IHBhaXJlZHxtYXBRPj01IiwgQ0FUKSkgJT4lCiAgICBtdXRhdGUoTUFQU1RBVCA9IGlmZWxzZShncmVwbCgibWFwUT49NSIsIENBVCksICJNaXNtYXRjaCIsCiAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoInByb3Blcmx5IiwgQ0FUKSwgIlByb3BfUGFpcmVkIiwgIk1hcHBlZCIpKSkgJT4lCiAgICBtdXRhdGUoSW5kID0gYyhyZXAoMTpuLCBlYWNoID0gMykpKSAlPiUgCiAgICAjIG5vdCBzdXJlIGlmIGV4dHJhIGluZGl2aWR1YWwgaW4gdGhlcmUgc29tZWhvdwogICAgc2VsZWN0KDQsIDMsIDEpICU+JQogICAgc3ByZWFkKE1BUFNUQVQsIE5fUmVhZHMpCgogIGRmbGlzdF9mbGFnW1tkZl1dIDwtIHgKfQoKIyBDcmVhdGUgbmV3IGRhdGFmcmFtZXMgd2l0aCBzdW1tYXJ5IHN0YXRzIGFuZCBhZGQgdG8gbWFpbiBmaW5hbCBkYXRhIGZyYW1lCk1hcFN0YXRzLmZsYWcgPC0gZGF0YS5mcmFtZSgpCgpmb3IgKGRmIGluIDE6bGVuZ3RoKGRmbGlzdF9mbGFnKSl7CiAgeCA9IHN1bW1hcml6ZShkZmxpc3RfZmxhZ1tbZGZdXSwgU3VtX01hcHBlZCA9IHN1bShNYXBwZWQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlYWRzX01hcHBlZCA9IG1lYW4oTWFwcGVkKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdW1fUGFpcmVkID0gc3VtKFByb3BfUGFpcmVkKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNZWFuX1BhaXJlZCA9IG1lYW4oUHJvcF9QYWlyZWQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN1bV9NaXNtYXRjaCA9IHN1bShNaXNtYXRjaCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTWVhbl9NaXNtYXRjaCA9IG1lYW4oTWlzbWF0Y2gpKSAlPiUKICBtdXRhdGUoTGliID0gbGliW2RmXSkgJT4lCiAgc2VsZWN0KDcsIDE6NikKICBNYXBTdGF0cy5mbGFnIDwtIGJpbmRfcm93cyhNYXBTdGF0cy5mbGFnLCB4KQp9CgojIHdyaXRlIHRvIGZpbGUKd3JpdGUudGFibGUoTWFwU3RhdHMuZmxhZywgInJlc3VsdHMvTWFwU3RhdHMuZmxhZyIsIHF1b3RlID0gRkFMU0UpCgpNYXBTdGF0cy5mbGFnCgojIGNvbWJpbmUgZmlsZXMKbWFwc3RhdHMgPC0gbGVmdF9qb2luKE1hcFN0YXRzLmlkeCwgTWFwU3RhdHMuZmxhZykgJT4lCiAgbXV0YXRlKFBST1BfTUlTTUFUQ0ggPSBTdW1fTWlzbWF0Y2gvU3VtX01hcHBlZCkKCiMgd3JpdGUgc3VtbWFyeSBzdGF0cyBmaWxlCndyaXRlLnRhYmxlKG1hcHN0YXRzLCBmaWxlID0gInJlc3VsdHMvQldBX21hcHBpbmcuc3RhdHMiLCBxdW90ZSA9IEZBTFNFLCBzZXAgPSAiICIpCgpgYGAKCiMjIyBFdmFsdWF0ZSAmIGNvbXBhcmUgbWFwcGluZyByZXN1bHRzCgpDb21wYXJlIG51bWJlciBvZiByZWZlcmVuY2UgY29udGlncyBmb3Igd2hpY2ggbm8gcmVhZHMgbWFwcGVkIHRvIHBlciBsaWJyYXJ5LgoKYGBge3IgcGxvdCBtYXBzdGF0cywgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTJ9CgojIHBsb3Qgbm8gb2YgbG9jaSB2cyAiZW1wdHkiIGxvY2kKcDEgPC0gZ2dwbG90KG1hcHN0YXRzLCBhZXMoeCA9IExpYiwgeSA9IFBST1BfRU1QVFkpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDYpKSArCiAgbGFicyh4ID0gIiIsIHkgPSAiJSBjb250aWdzIHcvbm8gcmVhZHMgbWFwcGVkIikgKwogIHRoZW1lX3N0YW5kYXJkCgpwMiA8LSBnZ3Bsb3QobWFwc3RhdHMsIGFlcyh4ID0gTGliLCB5ID0gUmVhZHNfTWFwcGVkKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBsYWJzKHggPSAibGlicmFyeSIsIHkgPSAibWVhbiByZWFkcyBtYXBwZWQgcGVyIGluZHYiKSArCiAgdGhlbWVfc3RhbmRhcmQKCnAzIDwtIGdncGxvdChtYXBzdGF0cywgYWVzKHggPSBMaWIsIHkgPSBQUk9QX01JU01BVENIKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBsYWJzKHggPSAiIix5ID0gIiUgcmVhZHMgbm90IG1hcHBlZCBhcyBwYWlyIikgKwogIHRoZW1lX3N0YW5kYXJkCgptdWx0aXBsb3QocDEsIHAyLCBwMywgY29scyA9IDMpCgpgYGAKCiMgU05QIGNhbGxpbmcKClRyYW5zZmVyIGNvcHkgb2YgYHJlZmVyZW5jZS5mYXN0YWAgYW5kIGFzc29jaWF0ZWQgZmlsZXMgZm9yIEsxID0gNCBhbmQgSzIgPSAxIGludG8gU05QIGNhbGxpbmcgZGlyZWN0b3J5LgoKQ3JlYXRlIHN5bWxpbmtzIGZyb20gYWxsIGBmcWAsIGBiYW1gIGFuZCBgYmFtLmJhaWAtZmlsZXMgZm9yIGVhY2ggc2VwYXJhdGVseSBtYXBwZWQgbGlicmFyeSBpbiBgU05QX0NhbGxpbmdgIGZvbGRlci4KCmBgYHtiYXNoIHNvZnRsaW5rIGZpbGVzLCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQoKbG4gLXMgL2hvbWUvc29sZWFyeS9CVUxMU0hBUktTL0NMRV9QT1BHRU4vZGF0YS9TRVEvQ0xFLTIvKi5mcS5neiAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL1NOUF9DYWxsaW5nCmxuIC1zIC9ob21lL3NvbGVhcnkvQlVMTFNIQVJLUy9DTEVfUE9QR0VOL2RhdGEvU0VRL0NMRS0yLyouYmFtKiAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL1NOUF9DYWxsaW5nCgpsbiAtcyAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL1NFUS9DTEUtMy8qLmZxLmd6IC9ob21lL3NvbGVhcnkvQlVMTFNIQVJLUy9DTEVfUE9QR0VOL2RhdGEvU05QX0NhbGxpbmcKbG4gLXMgL2hvbWUvc29sZWFyeS9CVUxMU0hBUktTL0NMRV9QT1BHRU4vZGF0YS9TRVEvQ0xFLTMvKi5iYW0qIC9ob21lL3NvbGVhcnkvQlVMTFNIQVJLUy9DTEVfUE9QR0VOL2RhdGEvU05QX0NhbGxpbmcKCiMgcmVtb3ZlIHVubmVjZXNzYXJ5IGZpbGVzCmNkIC9ob21lL3NvbGVhcnkvQlVMTFNIQVJLUy9DTEVfUE9QR0VOL2RhdGEvU05QX0NhbGxpbmcKcm0gY2F0LVJSRy5iYW0qCgpgYGAKCkV4ZWN1dGUgYGREb2NlbnRgIGZyb20gd2l0aGluIGBTTlBfQ2FsbGluZ2AtZm9sZGVyIHRvIGNhbGwgdmFyaWFudHMgYWNyb3NzIGFsbCBpbmRpdmlkdWFscyAoYWxsIGxpYnJhcmllcykuCgpgYGB7YmFzaCBjYWxsIFNOUHMsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CgpjZCAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL1NOUF9DYWxsaW5nCmREb2NlbnQKCmBgYAoKRmlsZSBgVG90YWxyYXdTTlBzLnZjZmAgY29udGFpbnMgYWxsIHJhdyBTTlAvSU5ERUwgY2FsbHMuIERvIG5vdCBuZWVkIHRvIGtlZXAgbGlua3Mgb2YgYGZxLmd6YC0sIGBiYW1gLSwgYC5iYW0uYmFpYC1maWxlcyBhZnRlciBTTlBzIGhhdmUgYmVlbiBjYWxsZWQuIENvcHkgYFRvdGFsUmFTTlBzLnZjZmAgdG8gYFZDRl9GaWx0ZXJpbmdgIGZvciBTTlAgZmlsdGVyaW5nLgoKYGBge2Jhc2ggY29weSBUb3RhbFJhd1NOUHMgZm9yIGZpbHRlcmluZywgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KCmNkIC9ob21lL3NvbGVhcnkvQlVMTFNIQVJLUy9DTEVfUE9QR0VOL2RhdGEvU05QX0NhbGxpbmcKcm0gKmZxLmd6ICpiYW0qCgpjcCAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL1NOUF9DYWxsaW5nL1RvdGFsUmF3U05Qcy52Y2YgL2hvbWUvc29sZWFyeS9CVUxMU0hBUktTL0NMRV9QT1BHRU4vZGF0YS9WQ0YvdGVtcC8KCmBgYAoKIyBTTlAgZmlsdGVyaW5nCgpkRG9jZW50IHVzZXMgRnJlZUJheWVzIHRvIGNhbGwgU05QcyBhbmQgd3JpdGUgYSBWQ0YtZmlsZSBgVG90YWxyYXdTTlBzLnZjZmAuIFRoaXMgZGF0YSBzZXQgd2FzIGZpbHRlcmVkIHRvIHJlbW92ZSBsb3cgcXVhbGl0eSBhbmQgYXJ0ZWZhY3R1YWwgU05QIHNpdGVzLCBwYXJhbG9ncyBhbmQgbG93IHF1YWxpdHkgaW5kaXZpZHVhbHMgYmFzZWQgb24gbGV2ZWxzIG9mIG1pc3NpbmcgZGF0YSwgbWluaW11bS9tYXhpbXVtIHJlYWQgZGVwdGgsIGdlbm90eXBlIGNhbGwgcmF0ZSwgYW5kIG1pbm9yIGFsbGVsZSBmcmVxdWVuY2llcy4gQ29udGlncyBtYXkgY29udGFpbiBtb3JlIHRoYW4gb25lIFNOUDsgdGhlIHNjcmlwdCBgcmFkX2hhcGxvdHlwZXIucGxgIHdhcyB1c2VkIHRvIGNyZWF0ZSBoYXBsb3R5cGVzIGZvciBlYWNoIGxvY3VzLgoKIyMgUmF3IGRhdGEgCgojIyMgSW5kaXZpZHVhbHMvUG9wdWxhdGlvbnMgc2FtcGxlZAoKR2VuZXJhdGUgYSBsaXN0IG9mIGFsbCBpbmRpdmlkdWFscyBpbmNsdWRlZCBpbiB0aGUgU05QIGRhdGEgc2V0IGNhbGxlZCBieSBGcmVlQmF5ZXMgaW4gdGhlIGREb2NlbnQgcGlwZWxpbmUuCgpgYGB7YmFzaCB3cml0ZSByYXcgaW5kdn0KCnZjZnNhbXBsZW5hbWVzIGRhdGEvVkNGL3RlbXAvVG90YWxSYXdTTlBzLnZjZiA+IGRhdGEvVkNGL3Jhdy5pbmQKCmBgYAoKVXNlIGBJbmRfcmF3YCBmaWxlIHRvIHdyaXRlIHRleHQgZmlsZXMgb2YgaW5kaXZpZHVhbHMgaW4gZWFjaCBsaWJyYXJ5IGFuZCBpbiByZWdpb25hbCBncm91cGluZ3MuIAoKYGBge3IgY3JlYXRlIEluZCBmaWxlcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiMgaW1wb3J0IGluZGl2aWR1YWxzIGluIHJhdyBkYXRhIHNldCAtLS0tCkluZF9SQVcgPC0gcmVhZC50YWJsZSgiZGF0YS9WQ0YvcmF3LmluZCIsIAogICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gRkFMU0UsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGNvbC5uYW1lcyA9ICJMSUJfSUQiKSAlPiUKICBzZXBhcmF0ZShMSUJfSUQsIGludG8gPSBjKCJTUCIsICJXRUxMIiwgIlNBTVBMRV9JRCIpLCBzZXAgPSAiXyIsIHJlbW92ZSA9IEZBTFNFLCBleHRyYSA9ICJtZXJnZSIpCgojIFZpZXcoSW5kX1JBVykKCiMgaWRlbnRpZnkgZHVwbGljYXRlcwp0ZW1wIDwtIEluZF9SQVcgJT4lCiAgZ3JvdXBfYnkoU0FNUExFX0lEKSAlPiUKICBjb3VudCgpICU+JQogIGZpbHRlcihuID4gMSkgJT4lCiAgc2VsZWN0KFNBTVBMRV9JRCkKCmR1cCA8LSBJbmRfUkFXICU+JQogIGZpbHRlcihTQU1QTEVfSUQgJWluJSB0ZW1wJFNBTVBMRV9JRCkgJT4lCiAgc2VsZWN0KExJQl9JRCkKCiMgVmlldyh0ZW1wKQoKd3JpdGUudGFibGUoZHVwLCAiZGF0YS9WQ0YvZHVwbGljYXRlLmluZCIsCiAgICAgICAgICAgIGNvbC5uYW1lcyA9IEZBTFNFLCByb3cubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSkKCiMgY3JlYXRlIGZpbGVzIHdpdGggaW5kaXZpZHVhbHMgYnkgcG9wdWxhdGlvbiAtLS0tCgpTYW1wbGVJbmZvIDwtIHJlYWRfZGVsaW0oImRhdGEvUE9QR0VOL1NhbXBsZUluZm8udHh0IiwgZGVsaW0gPSAiXHQiKQoKSW5kX1JBVyA8LSBsZWZ0X2pvaW4oSW5kX1JBVywgU2FtcGxlSW5mbykKCnRlbXAgPC0gSW5kX1JBVyAlPiUKICBmaWx0ZXIoUE9QID09ICJBUkEiKSAlPiUKICBzZWxlY3QoTElCX0lEKQoKd3JpdGUudGFibGUodGVtcCwgImRhdGEvVkNGL0FSQS5pbmQiLAogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UpCgp0ZW1wIDwtIEluZF9SQVcgJT4lCiAgZmlsdGVyKFBPUCA9PSAiQ0MiKSAlPiUKICBzZWxlY3QoTElCX0lEKQoKd3JpdGUudGFibGUodGVtcCwgImRhdGEvVkNGL0NDLmluZCIsCiAgICAgICAgICAgIGNvbC5uYW1lcyA9IEZBTFNFLCByb3cubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSkKCnRlbXAgPC0gSW5kX1JBVyAlPiUKICBmaWx0ZXIoUE9QID09ICJHQUwiKSAlPiUKICBzZWxlY3QoTElCX0lEKQoKd3JpdGUudGFibGUodGVtcCwgImRhdGEvVkNGL0dBTC5pbmQiLAogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UpCgp0ZW1wIDwtIEluZF9SQVcgJT4lCiAgZmlsdGVyKFBPUCA9PSAiTUFUIikgJT4lCiAgc2VsZWN0KExJQl9JRCkKCndyaXRlLnRhYmxlKHRlbXAsICJkYXRhL1ZDRi9NQVQuaW5kIiwKICAgICAgICAgICAgY29sLm5hbWVzID0gRkFMU0UsIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFKQoKdGVtcCA8LSBJbmRfUkFXICU+JQogIGZpbHRlcihQT1AgPT0gIlNBIikgJT4lCiAgc2VsZWN0KExJQl9JRCkKCndyaXRlLnRhYmxlKHRlbXAsICJkYXRhL1ZDRi9TQS5pbmQiLAogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UpCgp0ZW1wIDwtIEluZF9SQVcgJT4lCiAgZmlsdGVyKFBPUCA9PSAiU0wiKSAlPiUKICBzZWxlY3QoTElCX0lEKQoKd3JpdGUudGFibGUodGVtcCwgImRhdGEvVkNGL1NMLmluZCIsCiAgICAgICAgICAgIGNvbC5uYW1lcyA9IEZBTFNFLCByb3cubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSkKCiMgY3JlYXRlIGZpbGVzIHdpdGggaW5kaXZpZHVhbHMgcGVyIGxpYnJhcnkgLS0tLQp0ZW1wIDwtIEluZF9SQVcgJT4lCiAgZmlsdGVyKGdyZXBsKCIyQSIsIFdFTEwpIHwKICAgICAgICAgZ3JlcGwoIjJCIiwgV0VMTCkpICU+JQogIHNlbGVjdChMSUJfSUQpCgp3cml0ZS50YWJsZSh0ZW1wLCAiZGF0YS9WQ0YvQ0xFMi5pbmQiLAogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UpCgp0ZW1wIDwtIEluZF9SQVcgJT4lCiAgZmlsdGVyKGdyZXBsKCIzQSIsIFdFTEwpIHwKICAgICAgICAgZ3JlcGwoIjNCIiwgV0VMTCkpICU+JQogIHNlbGVjdChMSUJfSUQpCgp3cml0ZS50YWJsZSh0ZW1wLCAiZGF0YS9WQ0YvQ0xFMy5pbmQiLAogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UpCgpgYGAKCiMjIyBDb21wYXJlIEluZGl2aWR1YWwgJiBTTlAgc3RhdHMKClVzZSBgVkNGdG9vbHNgIHRvIGNyZWF0ZSBzdGF0cyBmaWxlcyBmb3IgZGVwdGgsIG1pc3NpbmcgZGF0YSwgaGV0ZXJvenlnb3NpdHkgYW5kIHNpdGUgcXVhbGl0eSBmb3IgdGhlIHJhdyBkYXRhLgoKYGBge2Jhc2ggcXVlcnkgcmF3IHN0YXRzfQoKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9Ub3RhbFJhd1NOUHMudmNmIC0tb3V0IGRhdGEvVkNGL0NMRV9yYXcgLS1kZXB0aAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL1RvdGFsUmF3U05Qcy52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFX3JhdyAtLXNpdGUtbWVhbi1kZXB0aAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL1RvdGFsUmF3U05Qcy52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFX3JhdyAtLXNpdGUtcXVhbGl0eQp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL1RvdGFsUmF3U05Qcy52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFX3JhdyAtLW1pc3NpbmctaW5kdgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL1RvdGFsUmF3U05Qcy52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFX3JhdyAtLW1pc3Npbmctc2l0ZQp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL1RvdGFsUmF3U05Qcy52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFX3JhdyAtLWhldAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL1RvdGFsUmF3U05Qcy52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFX3JhdyAtLXNpbmdsZXRvbnMKCmBgYAoKQ29tcGFyZSByYXcgc3RhdHMuCiAgICAKYGBge3Igc3RhdHMgcmF3LCBmaWcuaGVpZ2h0PTIwLCBmaWcud2lkdGg9MTAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIGxvYWQgc3RhdHMgZmlsZXMgLS0tLQppbmRfc3RhdHNfcmF3IDwtIHJlYWQuaW5kLnN0YXRzKGRpciA9ICJkYXRhL1ZDRiIsIHZjZiA9ICJDTEVfcmF3IikgJT4lCiAgc2VwYXJhdGUoSU5EViwgaW50byA9IGMoIlNQIiwgIkxJQiIsICJTQU1QTEVfSUQiKSwgc2VwID0gIl8iLCByZW1vdmUgPSBGQUxTRSkKCmxvY19zdGF0c19yYXcgPC0gcmVhZC5sb2Muc3RhdHMoZGlyID0gImRhdGEvVkNGLyIsIHZjZiA9ICJDTEVfcmF3IikKCiMgcGxvdCBtaXNzaW5nIGRhdGEgcGVyIGluZHYgLS0tLQpwMSA8LSBnZ3Bsb3QoaW5kX3N0YXRzX3JhdywgYWVzKHggPSBNSVNTX0NMRV9yYXcpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAuMDEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKE1JU1NfQ0xFX3JhdywgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMC41KSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIm1pc3NpbmcgZGF0YSBwZXIgaW5kdiIpICsKICB0aGVtZV9zdGFuZGFyZAoKIyBwbG90IHJlYWQgZGVwdGggcGVyIGluZHYgLS0tLQpwMiA8LSBnZ3Bsb3QoaW5kX3N0YXRzX3JhdywgYWVzKHggPSBNRUFOX0RFUFRIX0NMRV9yYXcpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMCwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oTUVBTl9ERVBUSF9DTEVfcmF3LCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAyMCksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJtZWFuIHJlYWQgZGVwdGggcGVyIGluZHYiKSArCiAgdGhlbWVfc3RhbmRhcmQKCiMgcGxvdCBkZXB0aCB2cyBtaXNzaW5nIC0tLS0KcDMgPC0gZ2dwbG90KGluZF9zdGF0c19yYXcsIGFlcyh4ID0gTUVBTl9ERVBUSF9DTEVfcmF3LCB5ID0gTUlTU19DTEVfcmF3KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oTUVBTl9ERVBUSF9DTEVfcmF3LCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAyMCksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtZWFuKE1JU1NfQ0xFX3JhdywgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMC41KSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIm1lYW4gZGVwdGggcGVyIGluZHYiLCB5ID0gIiUgbWlzc2luZyBkYXRhIikgKwogIHRoZW1lX3N0YW5kYXJkCgojIHBsb3QgRmlzIHBlciBpbmR2IC0tLS0KcDQgPC0gZ2dwbG90KGluZF9zdGF0c19yYXcsIGFlcyh4ID0gRmlzX0NMRV9yYXcpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAuMDEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKEZpc19DTEVfcmF3LCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAwKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIkZpcyBwZXIgaW5kdiIpICsKICB0aGVtZV9zdGFuZGFyZAoKIyBwbG90IEZpcyB2cyBtaXNzaW5nIGRhdGEgcGVyIGluZHYgLS0tLQpwNSA8LSBnZ3Bsb3QoaW5kX3N0YXRzX3JhdywgYWVzKHggPSBGaXNfQ0xFX3JhdywgeSA9IE1JU1NfQ0xFX3JhdykpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKEZpc19DTEVfcmF3LCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAwKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1lYW4oTUlTU19DTEVfcmF3LCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSAwLjUpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAiRmlzIHBlciBpbmR2IiwgeSA9ICIlIG1pc3NpbmcgZGF0YSIpICsKICB0aGVtZV9zdGFuZGFyZAoKIyBwbG90IEZpcyB2cyBtZWFuIGRlcHRoIHBlciBpbmR2IC0tLS0KcDYgPC0gZ2dwbG90KGluZF9zdGF0c19yYXcsIGFlcyh4ID0gRmlzX0NMRV9yYXcsIHkgPSBNRUFOX0RFUFRIX0NMRV9yYXcpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihGaXNfQ0xFX3JhdywgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMCksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtZWFuKE1FQU5fREVQVEhfQ0xFX3JhdywgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMjApLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAiRmlzIHBlciBpbmR2IiwgeSA9ICJtZWFuIGRlcHRoIHBlciBpbmR2IikgKwogIHRoZW1lX3N0YW5kYXJkCgoKIyBwbG90IGRpc3RyaWJ1dGlvbiBtaXNzaW5nIGRhdGEgcGVyIGxvY3VzIC0tLS0KcDcgPC0gZ2dwbG90KGxvY19zdGF0c19yYXcsIGFlcyh4ID0gTUlTU19DTEVfcmF3KSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4wMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oTUlTU19DTEVfcmF3LCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAwLjEpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAiJSBtaXNzaW5nIGRhdGEgcGVyIGxvY3VzIikgKwogIHRoZW1lX3N0YW5kYXJkCgojIHBsb3QgZGlzdHJpYnV0aW9uIG1lYW4gcmVhZCBkZXB0aCAtLS0tCnA4IDwtIGdncGxvdChsb2Nfc3RhdHNfcmF3LCBhZXMoeCA9IE1FQU5fREVQVEhfQ0xFX3JhdykpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDIwLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihNRUFOX0RFUFRIX0NMRV9yYXcsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IDIwKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIm1lYW4gcmVhZCBkZXB0aCBwZXIgbG9jdXMiKSArCiAgdGhlbWVfc3RhbmRhcmQKCiMgcGxvdCByZWFkIGRlcHRoIHZzIG1pc3NpbmcgZGF0YSAtLS0tCnA5IDwtIGdncGxvdChsb2Nfc3RhdHNfcmF3LCBhZXMoeCA9IE1FQU5fREVQVEhfQ0xFX3JhdywgeSA9IE1JU1NfQ0xFX3JhdykpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKE1FQU5fREVQVEhfQ0xFX3JhdywgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMjApLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWVhbihNSVNTX0NMRV9yYXcsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IDAuMSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJtZWFuIGRlcHRoIHBlciBsb2N1cyIsIHkgPSAiJSBtaXNzaW5nIGRhdGEiKSArCiAgdGhlbWVfc3RhbmRhcmQKCiMgcGxvdCBkZXB0aCB2cyBTTlAgcXVhbGl0eSAtLS0tCnNpdGVfcXVhbCA8LSByZWFkLnRhYmxlKCJkYXRhL1ZDRi9DTEVfcmF3LmxxdWFsIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lCiAgbXV0YXRlKFBST0IgPSAxMF4oLVFVQUwvMTApKQoKdGVtcCA8LSBkYXRhLmZyYW1lKGxvY19zdGF0c19yYXckTUVBTl9ERVBUSF9DTEVfcmF3LCBzaXRlX3F1YWwkUVVBTCkgJT4lCiAgcmVuYW1lKGRlcHRoID0gbG9jX3N0YXRzX3Jhdy5NRUFOX0RFUFRIX0NMRV9yYXcsIHF1YWwgPSBzaXRlX3F1YWwuUVVBTCkKCnAxMCA8LSBnZ3Bsb3QodGVtcCwgYWVzKHggPSBkZXB0aCwgeSA9IHF1YWwpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKGRlcHRoLCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAyMCksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtZWFuKHF1YWwsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IDIwKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIm1lYW4gZGVwdGggcGVyIGxvY3VzIiwgeSA9ICJTTlAgcXVhbGl0eSIpICsKICB0aGVtZV9zdGFuZGFyZAoKIyBwbG90IG51bWJlciBvZiBTTlBzIHBlciBjb250aWcgdnMuIG1lYW4gZGVwdGggLS0tLQp0ZW1wIDwtIGxvY19zdGF0c19yYXcgJT4lCiAgY291bnQoQ0hSKQoKcDExIDwtIGxlZnRfam9pbih0ZW1wLCBsb2Nfc3RhdHNfcmF3KSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IG4sIHkgPSBNRUFOX0RFUFRIX0NMRV9yYXcpKSArCiAgbGFicyh4ID0gIm51bWJlciBvZiBTTlBzIHBlciBjb250aWciLCB5ID0gIm1lYW4gZGVwdGgiKSArCiAgdGhlbWVfc3RhbmRhcmQKCiMgcGxvdCBubyBvZiBTTlBzIHBlciBsb2N1cyAtLS0tCnAxMiA8LSBsb2Nfc3RhdHNfcmF3ICU+JQogIGNvdW50KENIUikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKyAKICBsYWJzKHggPSAibnVtYmVyIG9mIFNOUHMgcGVyIGxvY3VzIikgKwogIHRoZW1lX3N0YW5kYXJkCgptcmF3IDwtIG11bHRpcGxvdChwMSwgcDIsIHAzLCBwNCwgcDUsIHA2LCBwNywgcDgsIHA5LCBwMTAsIHAxMSwgcDEyLCBjb2xzPTIpCgpgYGAKCiMjIENob29zZSB0aHJlc2hvbGQgdmFsdWVzIGZvciBxdWFsaXR5IHNjb3JlLCBjb3ZlcmFnZSwgbWlzc2luZyBkYXRhLCBtaW5vciBhbGxlbGVzIGFuZCBtYXBwaW5nL3ZhcmlhbnQgY2FsbGluZyBhcnRpZmFjdHMKCiMjIyBGaWx0ZXIgMDogUmVtb3ZlIExRIGluZGl2aWR1YWxzCgpSZW1vdmUgKGtub3duKSBsb3cgcXVhbGl0eSBpbmRpdmlkdWFscyBmcm9tIGRhdGEgc2V0OgoKSWRlbnRpZnkgbG93IHF1YWxpdHkgaW5kaXZpZHVhbHMgdG8gcmVtb3ZlIGZyb20gdGhlIGRhdGEgc2V0OyBkZWZpbmVkIGFzIGluZGl2aWR1YWxzIHdpdGggYSBtZWFuIGNvdmVyYWdlIG9mIHRocmVlIG9yIGxlc3MgcmVhZHMgYWNyb3NzIGFsbCBsb2NpIGFuZCBtb3JlIHRoYW4gODUlIG1pc3NpbmcgZGF0YToKCgpgYGB7ciBscSBpbmR2fQoKTFFpbmR2IDwtIGluZF9zdGF0c19yYXcgJT4lCiAgZmlsdGVyKE1FQU5fREVQVEhfQ0xFX3JhdyA8IDUgfCBNSVNTX0NMRV9yYXcgPj0gMC43NSkgJT4lCiAgc2VsZWN0KElORFYpCgojIFZpZXcoTFFpbmR2KQoKd3JpdGVfZGVsaW0oTFFpbmR2LCAiZGF0YS9WQ0YvTFFfcmF3LmluZCIsIGRlbGltID0gIlx0IikKCmBgYAoKUmVtb3ZlIGxvdyBxdWFsaXR5IGluZGl2aWR1YWxzIGFuZCBkZWNvbXBvc2UgaW5kZWxzLgoKYGBge2Jhc2ggZmlsdGVyIGxxIGluZHYsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CgojIGRlY29tcG9zZSBpbmRlbHMKdmNmYWxsZWxpY3ByaW1pdGl2ZXMgZGF0YS9WQ0YvdGVtcC9Ub3RhbFJhd1NOUHMudmNmIC0ta2VlcC1pbmZvIC0ta2VlcC1nZW5vID4gZGF0YS9WQ0YvdGVtcC9DTEUucHJpbS52Y2YKCiMgcmV0YWluIG9ubHkgU05QcyAvIHJlbW92ZSBMUSBpbmRpdmlkdWFscwp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5wcmltLnZjZiAtLW91dCBkYXRhL1ZDRi90ZW1wL0NMRS5GMCAtLXJlbW92ZS1pbmRlbHMgLS1yZW1vdmUgZGF0YS9WQ0YvTFFfcmF3LmluZCAtLXJlY29kZSAtLXJlY29kZS1JTkZPLWFsbAoKIyBnZXQgZHVwbGljYXRlcyBzdGF0cwp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL1RvdGFsUmF3U05Qcy52Y2YgLS1vdXQgZGF0YS9WQ0YvRjAgLS1rZWVwIGRhdGEvVkNGL2R1cGxpY2F0ZS5pbmQgLS0wMTIKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9Ub3RhbFJhd1NOUHMudmNmIC0tb3V0IGRhdGEvVkNGL0YwIC0ta2VlcCBkYXRhL1ZDRi9kdXBsaWNhdGUuaW5kIC0tZ2Vuby1kZXB0aAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL1RvdGFsUmF3U05Qcy52Y2YgLS1vdXQgZGF0YS9WQ0YvRjAgLS1rZWVwIGRhdGEvVkNGL2R1cGxpY2F0ZS5pbmQgLS1zaW5nbGV0b25zCgojIHF1ZXJ5IHN0YXRzCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYwLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYwIC0tZGVwdGgKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjAucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuRjAgLS1zaXRlLW1lYW4tZGVwdGgKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjAucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuRjAgLS1taXNzaW5nLWluZHYKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjAucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuRjAgLS1taXNzaW5nLXNpdGUKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjAucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuRjAgLS1oZXQKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjAucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuRjAgLS1zaW5nbGV0b25zCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYwLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYwIC0tZ2Vuby1kZXB0aAoKYGBgCgpDb21wYXJlIHN0YXRzOgoKYGBge3Igc3RhdHMgRjAsIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD0xMCwgbWVzc2FnZT1UUlVFLCB3YXJuaW5nPVRSVUV9CgojIGxvYWQgc3RhdHMgZmlsZXMgLS0tLQppbmRfc3RhdHNfRjAgPC0gcmVhZC5pbmQuc3RhdHMoZGlyID0gImRhdGEvVkNGIiwgdmNmID0gIkNMRS5GMCIpICU+JQogIHNlcGFyYXRlKElORFYsIGludG8gPSBjKCJTUCIsICJMSUIiLCAiU0FNUExFX0lEIiksIHNlcCA9ICJfIiwgcmVtb3ZlID0gRkFMU0UsIGV4dHJhID0gIm1lcmdlIikKCmxvY19zdGF0c19GMCA8LSByZWFkLmxvYy5zdGF0cyhkaXIgPSAiZGF0YS9WQ0YvIiwgdmNmID0gIkNMRS5GMCIpCgpgYGAKCkltcG9ydCBzaW5nbGV0b25zIGFuZCBnZW5vdHlwZSBkZXB0aCBmaWxlIHRvIGNyZWF0ZSBsaXN0IG9mIGxvY2kgdG8gZXhjbHVkZS4KCmBgYHtyIHJlYWQgc2luZ2xldG9uIGdlbm90eXBlIGRlcHRoLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKIyBudW1iZXIgb2YgaW5kaXZpZHVhbHMKbiA8LSBucm93KGluZF9zdGF0c19GMCkrMgoKIyByZWFkIHNpbmdsZXRvbnMgZmlsZQpzaW5nbGV0b25zIDwtIHJlYWRfdGFibGUyKCJkYXRhL1ZDRi9DTEUuRjAuc2luZ2xldG9ucyIpICU+JQogIG11dGF0ZShWQVJJQU5UID0gaWZlbHNlKEFMTEVMRSAlaW4lIGMoIkEiLCAiVCIsICJDIiwgIkciKSwgIlNOUCIsICJJTkRFTCIpKQoKY2hyb20gPC0gdW5pcXVlKHNpbmdsZXRvbnMkQ0hST00pCgojIHJlYWQgZ2Vub3R5cGUgZGVwdGggZmlsZSBhbmQgam9pbiB3aXRoIHNpbmdsZXRvbnMKZ2RlcHRoIDwtIHJlYWRfdGFibGUyKCJkYXRhL1ZDRi9DTEUuRjAuZ2RlcHRoIikgJT4lCiAgZmlsdGVyKENIUk9NICVpbiUgY2hyb20pICU+JQogIGdhdGhlcihrZXkgPSBJTkRWLCB2YWx1ZSA9IERFUFRILCAzOm4pCgpzaW5nbGV0b25zIDwtIGxlZnRfam9pbihzaW5nbGV0b25zLCBnZGVwdGgpCiAgCmBgYAoKRGF0YSBzZXQgY29udGFpbnMgYG5yb3coc2luZ2xldG9ucylgIHNpbmdsZXRvbnMuCgpgYGB7ciBkb3VibGV0b25zfQoKIyBmaWx0ZXIgZG91YmxldG9ucwpkb3VibGV0b25zIDwtIHNpbmdsZXRvbnMgJT4lCiAgZmlsdGVyKGBTSU5HTEVUT04vRE9VQkxFVE9OYCA9PSAiRCIpCgpgYGAKCk9mIHRob3NlIGBucm93KGRvdWJsZXRvbnMpYCBhcmUgY2FsbGVkIGFzIGEgaG9tb3p5Z290ZSBpbiBvbmUgaW5kaXZpZHVhbC4KCmBgYHtyIHNpbmdsZXRvbnMgcGVyIGNvbnRpZywgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NH0KCiMgbnVtYmVyIG9mIGNvbnRpZ3MgaW4gZGF0YSBzZXQKY29udGlnc190b3RhbCA8LSBsb2Nfc3RhdHNfRjAgJT4lCiAgZGlzdGluY3QoQ0hST00pCgpjb250aWdzX3NpbmdsZXRvbnMgPC0gc2luZ2xldG9ucyAlPiUKICBkaXN0aW5jdChDSFJPTSkKCmNvbnRpZ3MgPC0gc2luZ2xldG9ucyAlPiUKICBjb3VudChDSFJPTSkKCmdncGxvdChjb250aWdzLCBhZXMoeCA9IG4pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsgCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4obiwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIm51bWJlciBvZiBzaW5nbGV0b25zIHBlciBsb2N1cyIpICsKICB0aGVtZV9zdGFuZGFyZAoKYGBgCgpUaGUgZGF0YSBzZXQgY29udGFpbnMgYG5yb3coY29udGlnc190b3RhbClgIGNvbnRpZ3MsIGBucm93KGNvbnRpZ3Nfc2luZ2xldG9ucylgIChgMnJvdW5kKCAobnJvdyhjb250aWdzX3NpbmdsZXRvbnMpL25yb3coY29udGlnc190b3RhbCkqMTAwKSwgZGlnaXRzID0gMilgJSkgY29udGFpbiBzaW5nbGV0b25zLgoKYGBge3IgZGlzdHJpYnV0aW9uIHNpbmdwbGV0b25zIHBlciBjb250aWcsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTZ9Cgpjb250aWdzIDwtIHNpbmdsZXRvbnMgJT4lCiAgZ3JvdXBfYnkoYFNJTkdMRVRPTi9ET1VCTEVUT05gKSAlPiUKICBjb3VudChDSFJPTSkKCmdncGxvdChjb250aWdzLCBhZXMoeCA9IG4pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsgCiAgZmFjZXRfZ3JpZCguIH4gYFNJTkdMRVRPTi9ET1VCTEVUT05gKSArCiAgbGFicyh4ID0gIm51bWJlciBvZiBkb3VibGV0b25zL3NpbmdlbHRvbnMgcGVyIGxvY3VzIikgKwogIHRoZW1lX3N0YW5kYXJkCgpgYGAKClRhcmdldCBpcyAyMCByZWFkcyBwZXIgbG9jdXM7IGlkZW50aWZ5IG51bWJlciBvZiBzaW5nbGV0b25zL2RvdWJsZXRvbnMgd2l0aCBkZXB0aCA8IDEwIHJlYWRzLgoKYGBge3J9Cgpjb3VudChzaW5nbGV0b25zLCBERVBUSCA8PSA1KQoKYGBgCgpEaXN0cml1YnRpb24gb2YgZ2Vub3R5cGUgZGVwdGggcGVyIHNpbmdsZXRvbi9kb3VibGV0b24uCgpgYGB7ciBkaXN0cmlidXRpb24gZ2VubyBkZXB0aCBwZXIgc2luZ2xldG9uLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD02fQoKZ2dwbG90KHNpbmdsZXRvbnMsIGFlcyh4ID0gREVQVEgpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAyMCwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZmFjZXRfZ3JpZCguIH4gYFNJTkdMRVRPTi9ET1VCTEVUT05gLCBzY2FsZXMgPSAiZnJlZSIpICsKICBsYWJzKHggPSAicmVhZCBkZXB0aCIpICsKICBzY2FsZV95X3NxcnQoKSArCiAgdGhlbWVfc3RhbmRhcmQKCmBgYAoKUXVhbnRpbGVzIGRpc3RyaWJ1dGlvbiBvZiByZWFkIGRlcHRoIGZvciBTTlAgbG9jaSBjYWxsZWQgaW4gb25seSBvbmUgaW5kaXZpZHVhbC4KCmBgYHtyIHF1YW50aWxlIHNpbmdsZXRvbiBkZXB0aH0KCnF1YW50aWxlKHNpbmdsZXRvbnMkREVQVEgsIHByb2JzID0gYyguMDUsIC4yNSwgLjUsIC43NSwgLjk1LCAuOTkpKQoKYGBgCgpEaXN0cmlidXRpb24gb2YgZG91YmxldG9ucyAoaG9tenlnb3VzIGdlbm90eXBlIGZvciB0aGF0IGluZGl2aWR1YWwpIGFuZCBzaW5nbGV0b25zIChoZXRlcm96eWdvdGUgZ2Vub3R5cGUpLgoKYGBge3IgZGlzdHJpYnV0aW9uIG5vIHNpbmdsZXRvbnMgcGVyIGluZHYsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTYsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpJbmQgPC0gc2luZ2xldG9ucyAlPiUKICBncm91cF9ieShgU0lOR0xFVE9OL0RPVUJMRVRPTmApICU+JQogIGNvdW50KElORFYpCgpJbmQgPC0gbGVmdF9qb2luKEluZCwgaW5kX3N0YXRzX0YwKQoKZ2dwbG90KEluZCwgYWVzKHggPSBuKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNTAsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKyAKICBmYWNldF9ncmlkKC4gfiBgU0lOR0xFVE9OL0RPVUJMRVRPTmApICsKICBsYWJzKHggPSAibnVtYmVyIG9mIHNpbmdsZXRvbnMgcGVyIGluZHYiKSArCiAgdGhlbWVfc3RhbmRhcmQKCmBgYAoKUXVhbnRpbGVzIGRpc3RyaWJ1dGlvbiBudW1iZXIgb2Ygc2luZ2xldG9ucyBjYWxsZWQgaW4gb25lIGluZGl2aWR1YWwuCgpgYGB7ciBxdWFudGlsZSBzaW5nbGV0b25zIHBlciBpbmR2fQoKcXVhbnRpbGUoSW5kJG4sIHByb2JzID0gYyguMDEsIC4wNSwgLjI1LCAuNSwgLjc1LCAuOTkpKQoKYGBgCgpDb21wYXJlIHRoZSBudW1iZXIgb2YgU05QcyB2cyBjb21wbGV4IHZhcmlhbnRzIChJTkRFTHMpLgoKYGBge3Igc2luZ2xldG9uIHZhcmlhbnQgdHlwZX0KCmNvdW50KHNpbmdsZXRvbnMsIFZBUklBTlQpCgpgYGAKCiMjIyBGaWx0ZXIgMTogQ29uZmlkZW5jZSBpbiBTTlAgY2FsbAogICAgClRoZSBRVUFMIGNvbHVtbiBvZiBhIFZDRiBmaWxlIGlzIGEgcGhyZWQgYmFzZWQgc2NvcmUgaW5kaWNhdGluZyB0aGUgcHJvYmFiaWxpdHkgdGhhdCB0aGUgdmFyaWFudCBzaG93biBpbiB0aGUgQUxUIGNvbHVtbiBpcyB3cm9uZy4gR2l2ZW4gdGhlIFBocmVkIHF1YWxpdHkgc2NvcmUgKFEpLCBhbmQgdGhlIHByb2JhYmlsaXR5IHRoYXQgYSBiYXNlIGlzIGluY29ycmVjdGx5IGNhbGxlZCAoUCksIFEgPSAtMTAoTG9nMTBQKS4KCkEgcXVhbGl0eSBzY29yZSBvZiAyMCBpbmRpY2F0ZXMsIGEgMSBpbiAxMDAgY2hhbmNlIHRoYXQgdGhlIFNOUCBzaXRlIGhhcyBiZWVuIGNhbGxlZCBpbmNvcnJlY3RseSAoaS5lLiA5OSUgcHJvYmFiaWxpdHkgdGhhdCBjb3JyZWN0IGNhbGwpLgoKRmlsdGVyIGxvY2kgd2l0aCBxdWFsaXR5IHNjb3JlIDwgMjAuIENvZGUgZ2Vub3R5cGVzIHdpdGggZ2Vub3R5cGUgY2FsbCBxdWFsaXR5IDwgMjAgb3IgIGdlbm90eXBlIGRlcHRoIDwgNSBhcyBtaXNzaW5nLgoKYGBge2Jhc2ggZmlsdGVyIExRIFNOUCBjYWxsc30KCiMgZmlsdGVyIExRIFNOUCBjYWxscwp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMC5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL3RlbXAvQ0xFLkYxIC0tbWluUSAyMCAtLW1pbkdRIDIwIC0tbWluRFAgNSAtLXJlY29kZSAtLXJlY29kZS1JTkZPLWFsbAoKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjEucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9GMSAtLWtlZXAgZGF0YS9WQ0YvZHVwbGljYXRlLmluZCAtLTAxMgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMS5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0YxIC0ta2VlcCBkYXRhL1ZDRi9kdXBsaWNhdGUuaW5kIC0tZ2Vuby1kZXB0aAoKIyBxdWVyeSBzdGF0cwp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMS5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMSAtLWRlcHRoCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxIC0tc2l0ZS1tZWFuLWRlcHRoCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxIC0tbWlzc2luZy1pbmR2CnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxIC0tbWlzc2luZy1zaXRlCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxIC0taGV0CnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxIC0tZ2Vuby1kZXB0aAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMS5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMSAtLXNpbmdsZXRvbnMKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjEucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuRjEgLS1pbmR2LWZyZXEtYnVyZGVuCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxIC0tZnJlcSAKCmBgYAoKQ29tcGFyZSBzdGF0cyBwb3N0LWZpbHRlcmluZy4KCmBgYHtyIHN0YXRzIEYxLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIGxvYWQgc3RhdHMgZmlsZXMgLS0tLQppbmRfc3RhdHNfRjEgPC0gcmVhZC5pbmQuc3RhdHMoZGlyID0gImRhdGEvVkNGIiwgdmNmID0gIkNMRS5GMSIpICU+JQogIHNlcGFyYXRlKElORFYsIGludG8gPSBjKCJTUCIsICJMSUIiLCAiU0FNUExFX0lEIiksIHNlcCA9ICJfIiwgcmVtb3ZlID0gRkFMU0UpCgpsb2Nfc3RhdHNfRjEgPC0gcmVhZC5sb2Muc3RhdHMoZGlyID0gImRhdGEvVkNGLyIsIHZjZiA9ICJDTEUuRjEiKQoKYGBgCgpEYXRhIHNldCBjb250YWlucyBgciBucm93KGluZF9zdGF0c19GMSlgIGluZGl2aWR1YWxzIGFuZCBgciBucm93KGxvY19zdGF0c19GMSlgIFNOUCBzaXRlcy4KClJlbW92aW5nIGxvdyBjb25maWRlbmNlIFNOUCBsb2NpIGFuZCBnZW5vdHlwZSBjYWxscyByZXN1bHRzIGluIGEgcmVkdWN0aW9uIGluIHRoZSBudW1iZXIgb2YgdGhlIChtYXhpbXVtKSBTTlBzIHBlciBsb2N1cy4KCmBgYHtyIE4gU05QcywgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9NSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCnAxIDwtIGxvY19zdGF0c19yYXcgJT4lCiAgY291bnQoQ0hSKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBuKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArIAogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKG4sIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDEwMCkpICsKICBsYWJzKHggPSAibnVtYmVyIG9mIFNOUHMgcGVyIGxvY3VzIikgKwogIHRoZW1lX3N0YW5kYXJkCgpwMiA8LSBsb2Nfc3RhdHNfRjEgJT4lCiAgY291bnQoQ0hSKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBuKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArIAogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKG4sIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDEwMCkpICsKICBsYWJzKHggPSAibnVtYmVyIG9mIFNOUHMgcGVyIGxvY3VzIikgKwogIHRoZW1lX3N0YW5kYXJkCgptMWEgPC0gbXVsdGlwbG90KHAxLCBwMiwgY29scz0xKQoKYGBgCgpDb2RpbmcgZ2Vub3R5cGVzIHdpdGggbG93IHJlYWQgZGVwdGggKDwgMykgYXMgbWlzc2luZywgcmVzdWx0cyBpbiBhbiBvdmVyYWxsIGluY3JlYXNlIGluIG1pc3NpbmcgZGF0YSBwZXIgbG9jdXMgYW5kIGEgc2hpZnQgaW4gbW9yZSBpbmRpdmlkdWFscyB3aXRoIG1vcmUgbWlzc2luZyBkYXRhIC0gdGhpcyBpcyBiZWNhdXNlIGluZGl2aWR1YWxzIChhbmQgbG9jaSkgd2l0aCBjb3ZlcmFnZSBpc3N1ZXMgYXJlIG5vdyBjaGFyYWN0ZXJpemVkIGJ5IGhpZ2hlciBtaXNzaW5nIGRhdGEuCgpgYGB7ciBtaXNzaW5nIGRhdGEgRjEsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTgsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgppcmF3IDwtIHJlYWQudGFibGUoImRhdGEvVkNGL0NMRV9yYXcuaW1pc3MiLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lCiAgc2VsZWN0KElORFYsIEZfTUlTUykgJT4lCiAgcmVuYW1lKHJhdyA9IEZfTUlTUykKCmltaXNzIDwtIHJlYWQudGFibGUoImRhdGEvVkNGL0NMRS5GMS5pbWlzcyIsCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUKICBzZWxlY3QoSU5EViwgRl9NSVNTKSAlPiUKICByZW5hbWUoRjEgPSBGX01JU1MpCgppbWlzcyA8LSBsZWZ0X2pvaW4oaW1pc3MsIGlyYXcpCgpwMSA8LSBnZ3Bsb3QoaW1pc3MsIGFlcyh4ID0gcmF3LCB5ID0gRjEpKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDEpICsKICBnZW9tX2FibGluZShzbG9wZSA9IDEsIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDEpKSArCiAgbGFicyh4ID0gImluZHYgbWlzc2luZyBkYXRhIHJhdyIsIHkgPSAiaW5kdiBtaXNzaW5nIGRhdGEgRjEiKSArCiAgdGhlbWVfc3RhbmRhcmQKCmxyYXcgPC0gcmVhZC50YWJsZSgiZGF0YS9WQ0YvQ0xFX3Jhdy5sbWlzcyIsCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUKICBzZWxlY3QoQ0hSLCBQT1MsIEZfTUlTUykgJT4lCiAgcmVuYW1lKHJhdyA9IEZfTUlTUykKCmxtaXNzIDwtIHJlYWQudGFibGUoImRhdGEvVkNGL0NMRS5GMS5sbWlzcyIsCiAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUKICBzZWxlY3QoQ0hSLCBQT1MsIEZfTUlTUykgJT4lCiAgcmVuYW1lKEYxID0gRl9NSVNTKQoKbG1pc3MgPC0gbGVmdF9qb2luKGxtaXNzLCBscmF3KQoKcDIgPC0gZ2dwbG90KGxtaXNzLCBhZXMoeCA9IHJhdywgeSA9IEYxKSkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxKSArCiAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAxKSkgKwogIGxhYnMoeCA9ICJsb2NpIG1pc3NpbmcgZGF0YSByYXciLCB5ID0gImxvY2kgbWlzc2luZyBkYXRhIEYxIikgKwogIHRoZW1lX3N0YW5kYXJkCgptMWIgPC0gbXVsdGlwbG90KHAxLCBwMiwgY29scz0yKQoKYGBgCgpDb21wYXJlIGRlcHRoIGluZGl2aWR1YWxzIGFmdGVyIHJlbW92aW5nIExRIFNOUCBsb2NpIGFuZCBnZW5vdHlwZXMuCgpgYGB7ciBkZXB0aCBGMSwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9NSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCmlyYXcgPC0gcmVhZC50YWJsZSgiZGF0YS9WQ0YvQ0xFX3Jhdy5pZGVwdGgiLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lCiAgc2VsZWN0KElORFYsIE1FQU5fREVQVEgpICU+JQogIHJlbmFtZShyYXcgPSBNRUFOX0RFUFRIKQoKaWRlcHRoIDwtIHJlYWQudGFibGUoImRhdGEvVkNGL0NMRS5GMS5pZGVwdGgiLAogICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lCiAgc2VsZWN0KElORFYsIE1FQU5fREVQVEgpICU+JQogIHJlbmFtZShGMSA9IE1FQU5fREVQVEgpCgppZGVwdGggPC0gbGVmdF9qb2luKGlyYXcsIGlkZXB0aCkKCmdncGxvdChpZGVwdGgsIGFlcyh4ID0gcmF3LCB5ID0gRjEpKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDEpICsKICBnZW9tX2FibGluZShzbG9wZSA9IDEsIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAxNTApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMTUwKSkgKwogIGxhYnMoeCA9ICJtZWFuIGRlcHRoIGluZCByYXciLCB5ID0gIm1lYW4gZGVwdGggaW5kIEYxIikgKwogIHRoZW1lX3N0YW5kYXJkCgpgYGAKCiMjIyBGaWx0ZXIgMjogR2Vub3R5cGUgY2FsbCByYXRlIGFuZCBhbGxvd2VkIG1pc3NpbmcgZGF0YSBwZXIgaW5kdgoKUmVtb3ZlIGxvY2kgd2l0aCBnZW5vdHlwZSBjYWxsIHJhdGUgPCA1MCUgYW5kIG1lYW4gZGVwdGggPCAyMCByZWFkcyBhY3Jvc3MgYWxsIGluZGl2aWR1YWxzLgoKYGBge2Jhc2ggMmEgZ2VubyA8IDUwfQoKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjEucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi90ZW1wL0NMRS5GMmEgLS1tYXgtbWlzc2luZyAwLjUgLS1taW4tbWVhbkRQIDE1IC0tcmVjb2RlIC0tcmVjb2RlLUlORk8tYWxsCgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMmEucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9GMmEgLS1rZWVwIGRhdGEvVkNGL2R1cGxpY2F0ZS5pbmQgLS0wMTIKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjJhLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvRjJhIC0ta2VlcCBkYXRhL1ZDRi9kdXBsaWNhdGUuaW5kIC0tZ2Vuby1kZXB0aAoKIyBsaWJyYXJ5IENMRS00CnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYyYS5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL3RlbXAvQ0xFMiAtLWtlZXAgZGF0YS9WQ0YvQ0xFMi5pbmQgIC0tcmVjb2RlIC0tcmVjb2RlLUlORk8tYWxsCgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRTIucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUyIC0tbWlzc2luZy1zaXRlCgojIGxpYnJhcnkgQ0xFLTUKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjJhLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvdGVtcC9DTEUzIC0ta2VlcCBkYXRhL1ZDRi9DTEUzLmluZCAgLS1yZWNvZGUgLS1yZWNvZGUtSU5GTy1hbGwKCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFMy5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRTMgLS1taXNzaW5nLXNpdGUKCiMgQXJhbnNhcyBCYXkKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjJhLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvdGVtcC9BUkEgLS1rZWVwIGRhdGEvVkNGL0FSQS5pbmQgIC0tcmVjb2RlIC0tcmVjb2RlLUlORk8tYWxsCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQVJBLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQVJBIC0tbWlzc2luZy1zaXRlCgojIENvcnB1cyBDaHJpc3RpIEJheQp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMmEucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi90ZW1wL0NDIC0ta2VlcCBkYXRhL1ZDRi9DQy5pbmQgIC0tcmVjb2RlIC0tcmVjb2RlLUlORk8tYWxsCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0MucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DQyAtLW1pc3Npbmctc2l0ZQoKIyBHYWx2ZXN0b24gQmF5CnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYyYS5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL3RlbXAvR0FMIC0ta2VlcCBkYXRhL1ZDRi9HQUwuaW5kICAtLXJlY29kZSAtLXJlY29kZS1JTkZPLWFsbAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0dBTC5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0dBTCAtLW1pc3Npbmctc2l0ZQoKIyBNYXRhZ29yZGEgQmF5CnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYyYS5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL3RlbXAvTUFUIC0ta2VlcCBkYXRhL1ZDRi9NQVQuaW5kICAtLXJlY29kZSAtLXJlY29kZS1JTkZPLWFsbAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL01BVC5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL01BVCAtLW1pc3Npbmctc2l0ZQoKIyBTYW4gQW50aW9uaW8KdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjJhLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvdGVtcC9TQSAtLWtlZXAgZGF0YS9WQ0YvU0EuaW5kICAtLXJlY29kZSAtLXJlY29kZS1JTkZPLWFsbAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL1NBLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvU0EgLS1taXNzaW5nLXNpdGUKCiMgU2FiaW5lIExha2UKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjJhLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvdGVtcC9TTCAtLWtlZXAgZGF0YS9WQ0YvU0wuaW5kICAtLXJlY29kZSAtLXJlY29kZS1JTkZPLWFsbAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL1NMLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvU0wgLS1taXNzaW5nLXNpdGUKCmBgYAoKQ29tcGFyZSBkaXN0cmlidXRpb24gb2YgbWlzc2luZyBkYXRhIHBlciBsb2N1cyBwZXIgbGlicmFyeS4KCmBgYHtyIGxtaXNzIHBlciBsaWIsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIGNyZWF0ZSBlbXB0eSBsaXN0CmxvY19taXNzaW5nIDwtIGxpc3QoKQoKIyBpbXBvcnQgbWlzc2luZyBkYXRhIHBlciBsb2N1cwpsb2NfbWlzc2luZ1tbMV1dIDwtIHJlYWQudGFibGUoImRhdGEvVkNGL0NMRTIubG1pc3MiLCAKICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUKICBzZWxlY3QoQ0hSLCBQT1MsIEZfTUlTUykgJT4lCiAgbXV0YXRlKExJQiA9ICJDTEUyIikKCmxvY19taXNzaW5nW1syXV0gPC0gcmVhZC50YWJsZSgiZGF0YS9WQ0YvQ0xFMy5sbWlzcyIsIAogICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIHNlbGVjdChDSFIsIFBPUywgRl9NSVNTKSAlPiUKICBtdXRhdGUoTElCID0gIkNMRTMiKQoKIyBjcmVhdGUgZGF0YSBmcmFtZSB3aXRoIGFsbCBpbmZvcm1hdGlvbgpsb2NfbWlzc2luZyA8LSBsZHBseShsb2NfbWlzc2luZywgZGF0YS5mcmFtZSkKCmdncGxvdChsb2NfbWlzc2luZywgYWVzKHggPSBGX01JU1MpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAuMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IDAuNSksCiAgICAgICAgICAgICBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAibWlzc2luZyBkYXRhIHBlciBsb2N1cyIpICsKICBmYWNldF9ncmlkKExJQiB+IC4pICsKICB0aGVtZV9zdGFuZGFyZAoKYGBgCgpGbGFnIGxvY2kgdGhhdCB3ZXJlIG5vdCBjYWxsZWQgaW4gPiAyNSUgb2YgaW5kaXZpZHVhbHMgaW4gYSBnaXZlbiBsaWJyYXJ5LgoKYGBge3J9CgojIGlkZW50aWZ5IGxvY2kgd2l0aCBoaWdoIG1pc3NpbmcgZGF0YSBpbiBlYWNoIGxpYnJhcnkKU05QcyA8LSBmaWx0ZXIobG9jX21pc3NpbmcsIEZfTUlTUyA+IDAuNSkgJT4lCiAgYXJyYW5nZShDSFIsIFBPUykKCmNvdW50KFNOUHMsIExJQikKCkxRbG9jaV9saWIgPC0gU05QcyAlPiUKICBzZWxlY3QoQ0hSLCBQT1MpICU+JQogIHVuaXF1ZSgpCgpgYGAKCkEgdG90YWwgb2YgYHIgTFFsb2NpX2xpYmAgbG9jaSB3ZXJlIGNhbGxlZCBpbiBsZXNzIHRoYW4gNTAlIG9mIGluZGl2aWR1YWxzIGluIG9uZSBvciBtb3JlIGxpYnJhcmllcy4gTG9jaSBiZWluZyBpbmNvbnNpc3RlbnRseSBjYWxsZWQgYmV0d2VlbiBsaWJyYXJpZXMgY2FuIHJlc3VsdCBpbiBsaWJyYXJ5IGVmZmVjdHMuCgpDb21wYXJlIGRpc3RyaWJ1dGlvbiBvZiBtaXNzaW5nIGRhdGEgcGVyIGxvY3VzIHBlciBzYW1wbGUgbG9jYXRpb24uCgpgYGB7ciBsbWlzcyBwZXIgbG9jLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0zLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKIyBjcmVhdGUgZW1wdHkgbGlzdApsb2NfbWlzc2luZyA8LSBsaXN0KCkKCiMgaW1wb3J0IG1pc3NpbmcgZGF0YSBwZXIgbG9jdXMKbG9jX21pc3NpbmdbWzFdXSA8LSByZWFkLnRhYmxlKCJkYXRhL1ZDRi9BUkEubG1pc3MiLCAKICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUKICBzZWxlY3QoQ0hSLCBQT1MsIEZfTUlTUykgJT4lCiAgbXV0YXRlKFBPUCA9ICJBUkEiKQoKbG9jX21pc3NpbmdbWzJdXSA8LSByZWFkLnRhYmxlKCJkYXRhL1ZDRi9DQy5sbWlzcyIsIAogICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIHNlbGVjdChDSFIsIFBPUywgRl9NSVNTKSAlPiUKICBtdXRhdGUoUE9QID0gIkNDIikKCmxvY19taXNzaW5nW1szXV0gPC0gcmVhZC50YWJsZSgiZGF0YS9WQ0YvR0FMLmxtaXNzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lCiAgc2VsZWN0KENIUiwgUE9TLCBGX01JU1MpICU+JQogIG11dGF0ZShQT1AgPSAiR0FMIikKCmxvY19taXNzaW5nW1s0XV0gPC0gcmVhZC50YWJsZSgiZGF0YS9WQ0YvTUFULmxtaXNzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lCiAgc2VsZWN0KENIUiwgUE9TLCBGX01JU1MpICU+JQogIG11dGF0ZShQT1AgPSAiTUFUIikKCmxvY19taXNzaW5nW1s1XV0gPC0gcmVhZC50YWJsZSgiZGF0YS9WQ0YvU0EubG1pc3MiLCAKICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUKICBzZWxlY3QoQ0hSLCBQT1MsIEZfTUlTUykgJT4lCiAgbXV0YXRlKFBPUCA9ICJTQSIpCgpsb2NfbWlzc2luZ1tbNl1dIDwtIHJlYWQudGFibGUoImRhdGEvVkNGL1NMLmxtaXNzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lCiAgc2VsZWN0KENIUiwgUE9TLCBGX01JU1MpICU+JQogIG11dGF0ZShQT1AgPSAiU0wiKQoKIyBjcmVhdGUgZGF0YSBmcmFtZSB3aXRoIGFsbCBpbmZvcm1hdGlvbgpsb2NfbWlzc2luZyA8LSBsZHBseShsb2NfbWlzc2luZywgZGF0YS5mcmFtZSkKCmdncGxvdChsb2NfbWlzc2luZywgYWVzKHggPSBGX01JU1MpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAuMDI1LCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMC4yNSksCiAgICAgICAgICAgICBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAibWlzc2luZyBkYXRhIHBlciBsb2N1cyIpICsKICBmYWNldF9ncmlkKFBPUCB+IC4pICsKICBzY2FsZV95X3NxcnQoKSArCiAgdGhlbWVfc3RhbmRhcmQKCmBgYAoKRmxhZyBsb2NpIHRoYXQgd2VyZSBub3QgY2FsbGVkIGluID4gNzUlIG9mIGluZGl2aWR1YWxzIGF0IGEgZ2l2ZW4gc2FtcGxlIGxvY2F0aW9uLgoKYGBge3J9CgojIGlkZW50aWZ5IGxvY2kgd2l0aCBoaWdoIG1pc3NpbmcgZGF0YSBpbiBlYWNoIGxpYnJhcnkKU05QcyA8LSBmaWx0ZXIobG9jX21pc3NpbmcsIEZfTUlTUyA+IDAuMjUpICU+JQogIGFycmFuZ2UoQ0hSLCBQT1MpCgpjb3VudChTTlBzLCBQT1ApCgpMUWxvY2lfcG9wIDwtIFNOUHMgJT4lCiAgc2VsZWN0KENIUiwgUE9TKSAlPiUKICB1bmlxdWUoKQoKTFFsb2NpIDwtIGJpbmRfcm93cyhMUWxvY2lfbGliLCBMUWxvY2lfcG9wKSAlPiUKICB1bmlxdWUoKQoKIyBXcml0ZSBjb250aWcvcG9zaXRpb24gdG8gZmlsZQp3cml0ZS50YWJsZShMUWxvY2ksIGZpbGUgPSAiZGF0YS9WQ0YvTFFfRjJiLmxvY2kiLCAKICAgICAgICAgICAgY29sLm5hbWVzPSBGQUxTRSwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UpCgpgYGAKClJlbW92ZSBsb2NpIHRoYXQgd2VyZSBub3QgY29uc2lzdGVudGx5IGNhbGxlZCBhY3Jvc3MgYWxsIGxpYnJhcmllcyBhbmQgc2FtcGxlIGxvY2F0aW9ucy4KCmBgYHtiYXNoIGZpbHRlciBtaXNzaW5nIGJ5IGxpYn0KCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYyYS5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL3RlbXAvQ0xFLkYyYiAtLWV4Y2x1ZGUtcG9zaXRpb25zIGRhdGEvVkNGL0xRX0YyYi5sb2NpIC0tcmVjb2RlIC0tcmVjb2RlLUlORk8tYWxsCgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMmIucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9GMmIgLS1rZWVwIGRhdGEvVkNGL2R1cGxpY2F0ZS5pbmQgLS0wMTIKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjJiLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvRjJiIC0ta2VlcCBkYXRhL1ZDRi9kdXBsaWNhdGUuaW5kIC0tZ2Vuby1kZXB0aAoKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjJiLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYyYiAtLW1pc3NpbmctaW5kdgoKYGBgCgpJZGVudGlmeSBpbmRpdmlkdWFscyB3aXRoID4gNTAlIG1pc3NpbmcgZGF0YQoKYGBge3IgaW1pc3MgPiA0MCwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NH0KCiMgZGV0ZXJtaW5lIGN1dG9mZgppbWlzcyA8LSByZWFkLnRhYmxlKCJkYXRhL1ZDRi9DTEUuRjJiLmltaXNzIiwgCiAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAKCmdncGxvdChpbWlzcywgYWVzKHggPSBGX01JU1MpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjAxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjI1LCBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgdGhlbWVfc3RhbmRhcmQKCmltaXNzIDwtIGltaXNzICU+JQogIGZpbHRlcihGX01JU1MgPiAwLjI1KSAlPiUKICBzZWxlY3QoSU5EVikKClZpZXcoaW1pc3MpCgp3cml0ZS50YWJsZShpbWlzcywgImRhdGEvVkNGL0YyYl9MUS5pbmR2IiwKICAgICAgICAgICAgY29sLm5hbWVzID0gVFJVRSwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UpCgpgYGAKClJlbW92ZSBmbGFnZ2VkIGluZGl2aWR1YWxzLgoKYGBge2Jhc2ggZmlsdGVyIExRIGluZHZ9Cgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMmIucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi90ZW1wL0NMRS5GMiAtLXJlbW92ZSBkYXRhL1ZDRi9GMmJfTFEuaW5kdiAtLXJlY29kZSAtLXJlY29kZS1JTkZPLWFsbAoKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjIucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9GMiAtLWtlZXAgZGF0YS9WQ0YvZHVwbGljYXRlLmluZCAtLTAxMgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMi5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0YyIC0ta2VlcCBkYXRhL1ZDRi9kdXBsaWNhdGUuaW5kIC0tZ2Vuby1kZXB0aAoKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjIucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuRjIgLS1kZXB0aAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMi5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMiAtLXNpdGUtbWVhbi1kZXB0aAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMi5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMiAtLW1pc3NpbmctaW5kdgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMi5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMiAtLW1pc3Npbmctc2l0ZQp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMi5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMiAtLWhldAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMi5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMiAtLWdlbm8tZGVwdGgKCmBgYAoKQW5hbHl6ZSBzdGF0cyBwb3N0LWZpbHRlcmluZzoKCmBgYHtyIHN0YXRzIEYyLCBmaWcuaGVpZ2h0PTIwLCBmaWcud2lkdGg9MTAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIGxvYWQgc3RhdHMgZmlsZXMgLS0tLQppbmRfc3RhdHNfRjIgPC0gcmVhZC5pbmQuc3RhdHMoZGlyID0gImRhdGEvVkNGIiwgdmNmID0gIkNMRS5GMiIpICU+JQogIHNlcGFyYXRlKElORFYsIGludG8gPSBjKCJTUCIsICJMSUIiLCAiU0FNUExFX0lEIiksIHNlcCA9ICJfIiwgcmVtb3ZlID0gRkFMU0UpCgpsb2Nfc3RhdHNfRjIgPC0gcmVhZC5sb2Muc3RhdHMoZGlyID0gImRhdGEvVkNGLyIsIHZjZiA9ICJDTEUuRjIiKQoKYGBgCgpDb21wYXJlIGNoYW5nZSBpbiBtaXNzaW5nIGRhdGEgb2YgbG9jaSBhbmQgaW5kaXZpZHVhbHMuCgpgYGB7ciBtaXNzaW5nIGRhdGEgRjIsIGZpZy5oZWlnaHQ9My41LCBmaWcud2lkdGg9OCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiMgaW1wb3J0IGltaXNzIC0tLS0KCiMgY3JlYXRlIGVtcHR5IGxpc3QKaW1pc3MgPC0gbGlzdCgpCgojIGltcG9ydCBtaXNzaW5nIGRhdGEgcGVyIGxvY3VzCmltaXNzW1sxXV0gPC0gcmVhZC50YWJsZSgiZGF0YS9WQ0YvQ0xFX3Jhdy5pbWlzcyIsIAogICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIHNlbGVjdChJTkRWLCBGX01JU1MpICU+JQogIG11dGF0ZShGSUwgPSAicmF3IikKCmltaXNzW1syXV0gPC0gcmVhZC50YWJsZSgiZGF0YS9WQ0YvQ0xFLkYxLmltaXNzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lCiAgc2VsZWN0KElORFYsIEZfTUlTUykgJT4lCiAgbXV0YXRlKEZJTCA9ICJGMSIpCgppbWlzc1tbM11dIDwtIHJlYWQudGFibGUoImRhdGEvVkNGL0NMRS5GMi5pbWlzcyIsIAogICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIHNlbGVjdChJTkRWLCBGX01JU1MpICU+JQogIG11dGF0ZShGSUwgPSAiRjIiKQoKIyBjcmVhdGUgZGF0YSBmcmFtZSB3aXRoIGFsbCBpbmZvcm1hdGlvbgppbWlzcyA8LSBsZHBseShpbWlzcywgZGF0YS5mcmFtZSkgJT4lCiAgc3ByZWFkKGtleSA9IEZJTCwgdmFsdWUgPSBGX01JU1MpCgojIGltcG9ydCBsbWlzcyAtLS0tCgojIGNyZWF0ZSBlbXB0eSBsaXN0CmxtaXNzIDwtIGxpc3QoKQoKIyBpbXBvcnQgbWlzc2luZyBkYXRhIHBlciBsb2N1cwpsbWlzc1tbMV1dIDwtIHJlYWQudGFibGUoImRhdGEvVkNGL0NMRV9yYXcubG1pc3MiLCAKICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUKICBzZWxlY3QoQ0hSLCBQT1MsIEZfTUlTUykgJT4lCiAgbXV0YXRlKEZJTCA9ICJyYXciKQoKbG1pc3NbWzJdXSA8LSByZWFkLnRhYmxlKCJkYXRhL1ZDRi9DTEUuRjEubG1pc3MiLCAKICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUKICBzZWxlY3QoQ0hSLCBQT1MsIEZfTUlTUykgJT4lCiAgbXV0YXRlKEZJTCA9ICJGMSIpCgpsbWlzc1tbM11dIDwtIHJlYWQudGFibGUoImRhdGEvVkNGL0NMRS5GMi5sbWlzcyIsIAogICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIHNlbGVjdChDSFIsIFBPUywgRl9NSVNTKSAlPiUKICBtdXRhdGUoRklMID0gIkYyIikKCiMgY3JlYXRlIGRhdGEgZnJhbWUgd2l0aCBhbGwgaW5mb3JtYXRpb24KbG1pc3MgPC0gbGRwbHkobG1pc3MsIGRhdGEuZnJhbWUpICU+JQogIHNwcmVhZChrZXkgPSBGSUwsIHZhbHVlID0gRl9NSVNTKQoKIyBwbG90IC0tLS0KcDEgPC0gZ2dwbG90KGltaXNzLCBhZXMoeCA9IEYyLCB5ID0gcmF3KSkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxKSArCiAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMC4yNSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAwLjI1KSkgKwogIGxhYnMoeCA9ICJpbWlzcyBGMiIsIHkgPSAiaW1pc3MgcmF3IikgKwogIHRoZW1lX3N0YW5kYXJkCgpwMiA8LSBnZ3Bsb3QobG1pc3MsIGFlcyh4ID0gRjIsIHkgPSByYXcpKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDEpICsKICBnZW9tX2FibGluZShzbG9wZSA9IDEsIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAwLjI1KSkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDAuMjUpKSArCiAgbGFicyh4ID0gImxtaXNzIEYyIiwgeSA9ICJsbWlzcyByYXciKSArCiAgdGhlbWVfc3RhbmRhcmQKCm0yIDwtIG11bHRpcGxvdChwMSwgcDIsIGNvbHM9MikKCmBgYAoKIyMjIEZpbHRlciAzOiBGaWx0ZXIgbG9jaSBhbmQgaW5kaXZpZHVhbHMgYmFzZWQgb24gZGVwdGgsIHZhcmlhbmNlIGluIGRlcHRoIGFuZCBnZW5vdHlwZSBjYWxsIHJhdGUKCkRldGVybWluZSBtZWFuIGRlcHRoIGFuZCB2YXJpYW5jZSBwZXIgbG9jdXMgcGVyIGxpYnJhcnkuCgpgYGB7YmFzaCBkZXB0aCBkYXRhIHBlciBsaWJ9CgojIGxpYnJhcnkgQ0xFLTQKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjIucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi90ZW1wL0NMRTIgLS1rZWVwIGRhdGEvVkNGL0NMRTIuaW5kICAtLXJlY29kZSAtLXJlY29kZS1JTkZPLWFsbAoKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUyLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFMiAtLXNpdGUtbWVhbi1kZXB0aAoKIyBsaWJyYXJ5IENMRS01CnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYyLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvdGVtcC9DTEUzIC0ta2VlcCBkYXRhL1ZDRi9DTEUzLmluZCAgLS1yZWNvZGUgLS1yZWNvZGUtSU5GTy1hbGwKCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFMy5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRTMgLS1zaXRlLW1lYW4tZGVwdGgKCmBgYAoKQ29tcGFyZSBkaXN0cmlidXRpb24gb2YgZGVwdGggY292ZXJhZ2UgcGVyIGxvY3VzIHBlciBsaWJyYXJ5LiBJZGVudGlmeSBsb2NpIHRoYXQgZG8gbm90IGhhdmUgY29uc2lzdGVudCBjb3ZlcmFnZSBiZXR3ZWVuIGxpYnJhcmllcyAoY2FuIGxlYWQgdG8gbGlicmFyeSBlZmZlY3RzKSwgYW5kL29yIGFjcm9zcyBpbmRpdmlkdWFscy4gCgpgYGB7ciBkZXB0aCBwZXIgbG9jdXMgcGVyIGxpYiwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9NCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiMgY3JlYXRlIGVtcHR5IGxpc3QKbG9jX2RlcHRoIDwtIGxpc3QoKQoKIyBpbXBvcnQgZGVwdGggZGF0YSBwZXIgbG9jdXMKbG9jX2RlcHRoW1sxXV0gPC0gcmVhZC50YWJsZSgiZGF0YS9WQ0YvQ0xFMi5sZGVwdGgubWVhbiIsIAogICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIG11dGF0ZShMSUIgPSAiQ0xFMiIpCgpsb2NfZGVwdGhbWzJdXSA8LSByZWFkLnRhYmxlKCJkYXRhL1ZDRi9DTEUzLmxkZXB0aC5tZWFuIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lCiAgbXV0YXRlKExJQiA9ICJDTEUzIikKCiMgY3JlYXRlIGRhdGEgZnJhbWUgd2l0aCBhbGwgaW5mb3JtYXRpb24KbG9jX2RlcHRoIDwtIGxkcGx5KGxvY19kZXB0aCwgZGF0YS5mcmFtZSkKCmdncGxvdChsb2NfZGVwdGgsIGFlcyh4ID0gTUVBTl9ERVBUSCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDIwLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihNRUFOX0RFUFRILCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDIwLAogICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJtZWFuIGRlcHRoIHBlciBsb2N1cyIpICsKICBmYWNldF9ncmlkKExJQiB+IC4pICsKICB0aGVtZV9zdGFuZGFyZAoKYGBgCgpJZGVudGlmeSBsb2NpIHdpdGggbGFyZ2UgdmFyaWFuY2UgaW4gbWVhbiBkZXB0aCBhY3Jvc3MgbGlicmFyaWVzIGFuZC9vciBpbmRpdmlkdWFscyBieSBjYWxjdWxhdGluZyB0aGUgY29lZmZpY2llbnQgb2YgdmFyaWFuY2UgKFNURC9NRUFOKS4KCmBgYHtyIGNhbGN1bGF0ZSB2YXJpYXRpb24gaW4gZGVwdGgsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIGNhbGN1bGF0ZSBtZWFuIGRlcHRoIHdlaWdodGVkIGJ5IGxpYnJhcnkKZGVwdGhfY29tcCA8LSBsb2NfZGVwdGggJT4lCiAgZ3JvdXBfYnkoTElCKSAlPiUKICBkaXN0aW5jdChDSFJPTSwgLmtlZXBfYWxsID0gVFJVRSkgJT4lCiAgc2VsZWN0KC1QT1MpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieShDSFJPTSkgJT4lCiAgc3VtbWFyaXNlKE1FQU4gPSBtZWFuKE1FQU5fREVQVEgpLAogICAgICAgICAgICBTVEQgPSBzZChNRUFOX0RFUFRIKSkKCiMgbWVhbiBkZXB0aCBhY3Jvc3MgYWxsIGluZGl2aWR1YWxzCnRlbXAgPC0gcmVhZC50YWJsZSgiZGF0YS9WQ0YvQ0xFLkYyLmxkZXB0aC5tZWFuIiwgCiAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIGRpc3RpbmN0KENIUk9NLCAua2VlcF9hbGwgPSBUUlVFKSAlPiUKICBzZWxlY3QoLVBPUykgJT4lCiAgbXV0YXRlKFNURF9ERVBUSCA9IHNxcnQoVkFSX0RFUFRIKSkKCiMgY2FsY3VsYXRlIGNvZWZmaWNpZW50IG9mIHZhcmlhdGlvbgpkZXB0aF9jb21wIDwtIGxlZnRfam9pbihkZXB0aF9jb21wLCB0ZW1wKSAlPiUKICBtdXRhdGUoQ09FRkZfVkFSX0xJQiA9IFNURC9NRUFOKjEwMCwKICAgICAgICAgQ09FRkZfVkFSX0lORCA9IFNURF9ERVBUSC9NRUFOX0RFUFRIKjEwMCkKCmBgYAoKQ29tcGFyZSBtZWFuIGFjcm9zcyBhbGwgaW5kaXZpZHVhbCB0byBtZWFuIHdlaWdodGVkIGJ5IGxpYnJhcnkgYW5kIGNvZWZmaWNpZW50IG9mIHZhcmlhbmNlIG9mIHJlYWQgZGVwdGggYWNyb3NzIGluZGl2aWR1YWxzIGFuZCBiZXR3ZWVuIGxpYnJhcmllczoKCmBgYHtyIG1lYW4gc3RkIGRlcHRoLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD04LjV9CgpwMSA8LSBnZ3Bsb3QoZGVwdGhfY29tcCwgYWVzKHggPSBNRUFOX0RFUFRILCB5ID0gTUVBTikpICsKICBnZW9tX3BvaW50KHNoYXBlID0gMSkgKwogIGxhYnMoeCA9ICJtZWFuIGRlcHRoIHBlciBsb2N1cyIsIHkgPSAibWVhbiB3ZWlnaHRlZCBieSBsaWIiKSArCiAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJkYXJrcmVkIiwgc2l6ZSA9IDEpICsKICB0aGVtZV9zdGFuZGFyZAoKcDIgPC0gZ2dwbG90KGRlcHRoX2NvbXAsIGFlcyh4ID0gQ09FRkZfVkFSX0lORCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEwLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoQ09FRkZfVkFSX0lORCwgLjk5KSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJjb2VmZiB2YXIgZGVwdGggYWNyb3NzIGFsbCBpbmR2IikgKwogIHRoZW1lX3N0YW5kYXJkCgpwMyA8LSBnZ3Bsb3QoZGVwdGhfY29tcCwgYWVzKHggPSBDT0VGRl9WQVJfSU5ELCB5ID0gQ09FRkZfVkFSX0xJQikpICsKICBnZW9tX3BvaW50KHNoYXBlID0gMSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDIwMCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAxMDApKSArCiAgbGFicyh4ID0gImNvZWZmIHZhciBkZXB0aCBhY3Jvc3MgYWxsIGluZHYiLCB5ID0gImNvZWZmIHZhciBtZWFuIGRlcHRoIGJldHcgbGliIikgKwogIHRoZW1lX3N0YW5kYXJkCgpwNCA8LSBnZ3Bsb3QoZGVwdGhfY29tcCwgYWVzKHggPSBDT0VGRl9WQVJfTElCKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMTAsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBxdWFudGlsZShDT0VGRl9WQVJfTElCLCAuOTkpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gImNvZWZmIHZhciBkZXB0aCBiZXR3ZWVuIGxpYiIpICsKICB0aGVtZV9zdGFuZGFyZAoKbTNhIDwtIG11bHRpcGxvdChwMSwgcDIsIHAzLCBwNCwgY29scz0yKQoKYGBgCgpJZiBsb2NpIGhhdmUgY29uc2lzdGVudCBjb3ZlcmFnZSBhY3Jvc3MgbG9jaSB0aGUgbWVhbiByZWFkIGRlcHRoIHBlciBsb2N1cyBhY3Jvc3MgYWxsIGluZGl2aWR1YWxzIGFuZCB3ZWlnaHRlZCBieSBsaWJyYXJ5IHNob3VsZCBmYWxsIG9uIHRoZSByZWQgZGlhZ29uYWwuIAoKRmxhZyBsb2NpIHRoYXQgaGF2ZSBoaWdoIHZhcmlhdGlvbiBpbiBtZWFuIGNvdmVyYWdlIGJldHdlZW4gaW5kaXZpZHVhbHMgYW5kIGJldHdlZW4gbGlicmFyaWVzLgoKYGBge3IgTFEgbG9jaSB2YXJpYXRpb24gY292ZXJhZ2V9CgojIGlkZW50aWZ5IGxvY2kgaGlnaCB2YXJpYXRpb24gaW4gY292ZXJhZ2UKY29udGlnc192YXIgPC0gZGVwdGhfY29tcCAlPiUKICBmaWx0ZXIoQ09FRkZfVkFSX0xJQiA+IDkwIHwgQ09FRkZfVkFSX0lORCA+IDEyMCkKClNOUHNfdmFyIDwtIGZpbHRlcihsb2NfZGVwdGgsIENIUk9NICVpbiUgY29udGlnc192YXIkQ0hST00pICU+JQogIGRpc3RpbmN0KENIUk9NLCBQT1MpCgojIFdyaXRlIGNvbnRpZy9wb3NpdGlvbiB0byB0ZXh0IGZpbGUsIHVzZSBmaWxlIHdpdGggdmNmdG9vbHMgdG8gcmVtb3ZlIHBvc2l0aW9ucyBmcm9tIGRhdGFzZXQgCndyaXRlLnRhYmxlKFNOUHNfdmFyLCBmaWxlID0gImRhdGEvVkNGL0xRX0YzLmxvY2kiLCAKICAgICAgICAgICAgY29sLm5hbWVzPSBGQUxTRSwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UpCgpgYGAKClJlbW92ZSBsb2NpIGZsYWdnZWQgZm9yIGhpZ2ggdmFyaWFuY2UgaW4gZGVwdGggYWNyb3NzIGFsbCBpbmRpdmlkdWFscyBhbmQgYmV0d2VlbiBsaWJyYXJpZXMgKHJlbW92ZXMgbGlicmFyeSBlZmZlY3RzKSwgZmlsdGVyIGxvY2kgd2l0aCBtZWFuIGRlcHRoIDwgMjAgYW5kIGdlbm90eXBlIGNhbGwgcmF0ZSA8IDc1JQoKYGBge2Jhc2ggZmlsdGVyIHZhcmlhbmNlIGluIGNvdmVyYWdlIGxvY2l9Cgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMi5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL3RlbXAvQ0xFLkYzYSAtLWV4Y2x1ZGUtcG9zaXRpb25zIGRhdGEvVkNGL0xRX0YzLmxvY2kgLS1taW4tbWVhbkRQIDE1IC0tbWF4LW1pc3NpbmcgMC43NSAtLXJlY29kZSAtLXJlY29kZS1JTkZPLWFsbAoKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjNhLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvRjNhIC0ta2VlcCBkYXRhL1ZDRi9kdXBsaWNhdGUuaW5kIC0tMDEyCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYzYS5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0YzYSAtLWtlZXAgZGF0YS9WQ0YvZHVwbGljYXRlLmluZCAtLWdlbm8tZGVwdGgKCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYzYS5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GM2EgLS1kZXB0aAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GM2EucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuRjNhIC0tZ2Vuby1kZXB0aAoKYGBgCgpDb21wYXJlIG1lYW4gZGVwdGggYW5kIG51bWJlciBvZiBzaXRlcyBjYWxsZWQgcGVyIGluZGl2aWR1YWwuCgpgYGB7ciBpZGVwdGggc2l0ZXMgdnMgZGVwdGggSSwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NC41LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKcmVhZF90YWJsZTIoImRhdGEvVkNGL0NMRS5GM2EuaWRlcHRoIikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gTl9TSVRFUywgeSA9IE1FQU5fREVQVEgpKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDEsIHNpemUgPSAxLjUpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZGFya3JlZCIsIHNpemUgPSAwLjc1KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMjAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImRhcmtibHVlIiwgc2l6ZSA9IDAuNzUpICsKICBsYWJzKHggPSAibnVtYmVyIG9mIHNpdGVzIiwgeSA9ICJtZWFuIHJlYWQgZGVwdGgiKSArCiAgdGhlbWVfc3RhbmRhcmQKCmBgYAoKVXNlIGdlbm90eXBlIGRlcHRoIGZpbGUgdG8gaWRlbnRpZnkgaW5kaXZpZHVhbHMgd2l0aCBoaWdoIHZhcmlhbmNlIGluIHJlYWQgZGVwdGggYWNyb3NzIGxvY2kuCgpgYGB7ciB2YXJpYW5jZSBkZXB0aCBpbmRpdmlkdWFsIEl9CgojIG51bWJlciBvZiBpbmRpdmlkdWFscwpuIDwtIG5yb3coaW5kX3N0YXRzX0YyKSsxCgojIHJlYWQgZ2Vub3R5cGUgZGVwdGggJiBjb2RlIHZhbHVlcyA8IDUgYXMgbWlzc2luZwpnZGVwdGggPC0gcmVhZF90YWJsZTIoImRhdGEvVkNGL0NMRS5GM2EuZ2RlcHRoIikgJT4lCiAgc2VsZWN0KC1QT1MpICU+JQogIGRpc3RpbmN0KENIUk9NLCAua2VlcF9hbGwgPSBUUlVFKSAlPiUKICBnYXRoZXIoa2V5ID0gSU5EViwgdmFsdWUgPSBERVBUSCwgMzpuKSAlPiUKICBtdXRhdGUoREVQVEggPSBhcy5udW1lcmljKGdzdWIoLTEsIDAsIERFUFRIKSksCiAgICAgICAgIERFUFRIID0gYXMubnVtZXJpYyhnc3ViKCJcXDwxXFw+IiwgMCwgREVQVEgpKSwKICAgICAgICAgREVQVEggPSBhcy5udW1lcmljKGdzdWIoIlxcPDJcXD4iLCAwLCBERVBUSCkpLAogICAgICAgICBERVBUSCA9IGFzLm51bWVyaWMoZ3N1YigiXFw8M1xcPiIsIDAsIERFUFRIKSksCiAgICAgICAgIERFUFRIID0gYXMubnVtZXJpYyhnc3ViKCJcXDw0XFw+IiwgMCwgREVQVEgpKSkKCiMgY2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljcwppZGVwdGggPC0gZ2RlcHRoICU+JQogIGdyb3VwX2J5KElORFYpICU+JQogIHN1bW1hcml6ZShUT1RBTCA9IHN1bShERVBUSCksCiAgICAgICAgICAgIE1BWCA9IG1heChERVBUSCksCiAgICAgICAgICAgIE1FQU5fTk9OMCA9IG1lYW4oREVQVEhbREVQVEggPiAwXSksCiAgICAgICAgICAgIE1FQU4gPSBtZWFuKERFUFRIKSwKICAgICAgICAgICAgTUVESUFOID0gbWVkaWFuKERFUFRIKSwKICAgICAgICAgICAgVkFSID0gdmFyKERFUFRIKSwKICAgICAgICAgICAgU1REID0gc2QoREVQVEgpKSAlPiUKICBtdXRhdGUoQ09FRkZfVkFSID0gU1REL01FQU4sCiAgICAgICAgIFJBVElPX01FQU5fTUVESUFOID0gTUVBTi9NRURJQU4pCgppZGVwdGggPC0gbGVmdF9qb2luKGlkZXB0aCwgaW5kX3N0YXRzX0YyKQoKYGBgCgpDb21wYXJlIHZhcmlhYmlsaXR5IG9mIGRlcHRoIHdpdGhpbiBpbmRpdmlkdWFscy4KCmBgYHtyLCBmaWcuaGVpZ2h0PTE1LCBmaWcud2lkdGg9MTB9CgpwMSA8LSBnZ3Bsb3QoaWRlcHRoLCBhZXMoeCA9IE1BWCwgeSA9IFRPVEFMKSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJhdXRvIixsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSwgY29sb3IgPSAiZGFya3JlZCIpICsKICBnZW9tX3BvaW50KHNoYXBlID0gMSkgKwogIHRoZW1lX3N0YW5kYXJkCgpwMiA8LSBnZ3Bsb3QoaWRlcHRoLCBhZXMoeCA9IFRPVEFMLCB5ID0gVkFSKSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJhdXRvIixsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSwgY29sb3IgPSAiZGFya3JlZCIpICsKICBnZW9tX3BvaW50KHNoYXBlID0gMSkgKwogIHRoZW1lX3N0YW5kYXJkCgpwMyA8LSBnZ3Bsb3QoaWRlcHRoLCBhZXMoTUFYLCBNRURJQU4pKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImF1dG8iLGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxLCBjb2xvciA9ICJkYXJrcmVkIikgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxKSArCiAgdGhlbWVfc3RhbmRhcmQKCnA0IDwtIGdncGxvdChpZGVwdGgsIGFlcyh4ID0gTUVBTiwgeSA9IE1FRElBTikpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAiYXV0byIsbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEsIGNvbG9yID0gImRhcmtyZWQiKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDEpICsKICB0aGVtZV9zdGFuZGFyZAoKcDUgPC0gZ2dwbG90KGlkZXB0aCwgYWVzKHggPSBDT0VGRl9WQVIsIHkgPSBSQVRJT19NRUFOX01FRElBTikpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAiYXV0byIsbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEsIGNvbG9yID0gImRhcmtyZWQiKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDEpICsKICB0aGVtZV9zdGFuZGFyZAoKcDYgPC0gZ2dwbG90KGlkZXB0aCwgYWVzKE1FQU4pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1LCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAxNSwgY29sb3IgPSAiZGFya3JlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICB0aGVtZV9zdGFuZGFyZAoKcDcgPC0gZ2dwbG90KGlkZXB0aCwgYWVzKE1FRElBTikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDEwLCBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIHRoZW1lX3N0YW5kYXJkCgpwOCA8LSBnZ3Bsb3QoaWRlcHRoLCBhZXMoUkFUSU9fTUVBTl9NRURJQU4pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIHRoZW1lX3N0YW5kYXJkCgpwOSA8LSBnZ3Bsb3QoaWRlcHRoLCBhZXMoQ09FRkZfVkFSKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4wMjUsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIHRoZW1lX3N0YW5kYXJkCgpwMTAgPC0gZ2dwbG90KGlkZXB0aCwgYWVzKHggPSBDT0VGRl9WQVIsIHkgPSBNSVNTX0NMRS5GMikpICsKICBnZW9tX3BvaW50KHNoYXBlID0gMSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAuMjUsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImRhcmtyZWQiLCBzaXplID0gMC43NSkgKwogIHRoZW1lX3N0YW5kYXJkCgoKbTNiIDwtIG11bHRpcGxvdChwMSwgcDIsIHAzLCBwNCwgcDUsIHA2LCBwNywgcDgsIHA5LCBwMTAsIGNvbHM9MikKCmBgYAoKRmxhZyBMUSBpbmRpdmlkdWFscy4KCmBgYHtyfQoKTFFfZGVwdGggPC0gaWRlcHRoICU+JQogZmlsdGVyKE1FQU4gPD0gMTAgfCBNRURJQU4gPD0gNSkgJT4lCiBzZWxlY3QoSU5EVikKCkxRX2RlcHRoCiAKd3JpdGUudGFibGUoTFFfZGVwdGgsICJkYXRhL1ZDRi9GM19MUS5pbmR2IiwKICAgICAgICAgICBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSkKCmBgYAoKUmVtb3ZlIGZsYWdnZWQgbG9jaSBhbmQgaW5kaXZpZHVhbHMuCgpgYGB7YmFzaCBmaWx0ZXIgdmFyaWFuY2UgaW4gY292ZXJhZ2V9Cgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GM2EucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi90ZW1wL0NMRS5GMyAtLXJlbW92ZSBkYXRhL1ZDRi9GM19MUS5pbmR2IC0tcmVjb2RlIC0tcmVjb2RlLUlORk8tYWxsCgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMy5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0YzIC0ta2VlcCBkYXRhL1ZDRi9kdXBsaWNhdGUuaW5kIC0tMDEyCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYzLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvRjMgLS1rZWVwIGRhdGEvVkNGL2R1cGxpY2F0ZS5pbmQgLS1nZW5vLWRlcHRoCgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMy5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMyAtLWRlcHRoCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYzLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYzIC0tc2l0ZS1tZWFuLWRlcHRoCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYzLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYzIC0tbWlzc2luZy1pbmR2CnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYzLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYzIC0tbWlzc2luZy1zaXRlCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYzLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYzIC0taGV0CgpgYGAKCkNvbXBhcmUgc3RhdHMgcG9zdC1maWx0ZXJpbmc6CgpgYGB7ciBzdGF0cyBGMywgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTgsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIGxvYWQgc3RhdHMgZmlsZXMgLS0tLQppbmRfc3RhdHNfRjMgPC0gcmVhZC5pbmQuc3RhdHMoZGlyID0gImRhdGEvVkNGIiwgdmNmID0gIkNMRS5GMyIpICU+JQogIHNlcGFyYXRlKElORFYsIGludG8gPSBjKCJTUCIsICJMSUIiLCAiU0FNUExFX0lEIiksIHNlcCA9ICJfIiwgcmVtb3ZlID0gRkFMU0UpCgpsb2Nfc3RhdHNfRjMgPC0gcmVhZC5sb2Muc3RhdHMoZGlyID0gImRhdGEvVkNGLyIsIHZjZiA9ICJDTEUuRjMiKQoKIyBwbG90IG1pc3NpbmcgZGF0YSBwZXIgaW5kdiAtLS0tCnAxIDwtIGdncGxvdChpbmRfc3RhdHNfRjMsIGFlcyh4ID0gTUlTU19DTEUuRjMpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAuMDEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKE1JU1NfQ0xFLkYzLCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IDAuMjUpLAogICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJtaXNzaW5nIGRhdGEgcGVyIGluZHYiKSArCiAgdGhlbWVfc3RhbmRhcmQKCiMgcGxvdCByZWFkIGRlcHRoIHBlciBpbmR2IC0tLS0KcDIgPC0gZ2dwbG90KGluZF9zdGF0c19GMywgYWVzKHggPSBNRUFOX0RFUFRIX0NMRS5GMykpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEwLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihNRUFOX0RFUFRIX0NMRS5GMywgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAyMCksCiAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIm1lYW4gcmVhZCBkZXB0aCBwZXIgaW5kdiIpICsKICB0aGVtZV9zdGFuZGFyZAoKIyBwbG90IGRlcHRoIHZzIG1pc3NpbmcgLS0tLQpwMyA8LSBnZ3Bsb3QoaW5kX3N0YXRzX0YzLCBhZXMoeCA9IE1FQU5fREVQVEhfQ0xFLkYzLCB5ID0gTUlTU19DTEUuRjMpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihNRUFOX0RFUFRIX0NMRS5GMywgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAyMCksCiAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1lYW4oTUlTU19DTEUuRjMsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMC4yNSksCiAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIm1lYW4gZGVwdGggcGVyIGluZHYiLCB5ID0gIiUgbWlzc2luZyBkYXRhIikgKwogIHRoZW1lX3N0YW5kYXJkCgojIHBsb3QgZGlzdHJpYnV0aW9uIG1pc3NpbmcgZGF0YSBwZXIgbG9jdXMgLS0tLQpwNCA8LSBnZ3Bsb3QobG9jX3N0YXRzX0YzLCBhZXMoeCA9IE1JU1NfQ0xFLkYzKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4wMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oTUlTU19DTEUuRjMsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMC4xKSwKICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAiJSBtaXNzaW5nIGRhdGEgcGVyIGxvY3VzIikgKwogIHRoZW1lX3N0YW5kYXJkCgojIHBsb3QgZGlzdHJpYnV0aW9uIG1lYW4gcmVhZCBkZXB0aCAtLS0tCnA1IDwtIGdncGxvdChsb2Nfc3RhdHNfRjMsIGFlcyh4ID0gTUVBTl9ERVBUSF9DTEUuRjMpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAyMCwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oTUVBTl9ERVBUSF9DTEUuRjMsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMjApLAogICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJtZWFuIHJlYWQgZGVwdGggcGVyIGxvY3VzIikgKwogIHRoZW1lX3N0YW5kYXJkCgojIHBsb3QgcmVhZCBkZXB0aCB2cyBtaXNzaW5nIGRhdGEgLS0tLQpwNiA8LSBnZ3Bsb3QobG9jX3N0YXRzX0YzLCBhZXMoeCA9IE1FQU5fREVQVEhfQ0xFLkYzLCB5ID0gTUlTU19DTEUuRjMpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihNRUFOX0RFUFRIX0NMRS5GMywgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAyMCksCiAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1lYW4oTUlTU19DTEUuRjMsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMC4xKSwKICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAibWVhbiBkZXB0aCBwZXIgbG9jdXMiLCB5ID0gIiUgbWlzc2luZyBkYXRhIikgKwogIHRoZW1lX3N0YW5kYXJkCgptMyA8LSBtdWx0aXBsb3QocDEsIHAyLCBwMywgcDQsIHA1LCBwNiwgY29scz0yKQoKYGBgCgojIyMgRmlsdGVyIDQ6IEFsbGVsZSBiYWxhbmNlCgpBQjogQWxsZWxlIGJhbGFuY2UgYXQgaGV0ZXJvenlnb3VzIHNpdGVzOiBhIG51bWJlciBiZXR3ZWVuIDAgYW5kIDEgcmVwcmVzZW50aW5nIHRoZSByYXRpbyBvZiByZWFkcyBzaG93aW5nIHRoZSByZWZlcmVuY2UgYWxsZWxlIHRvIGFsbCByZWFkcywgY29uc2lkZXJpbmcgb25seSByZWFkcyBmcm9tIGluZGl2aWR1YWxzIGNhbGxlZCBhcyBoZXRlcm96eWdvdXMKCmBgYHtiYXNoIHF1ZXJ5IElORk8gc3RhdHMgSSwgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KCmN1dCAtZjggQ0xFLkYzLnJlY29kZS52Y2YgfCBncmVwIC1vZSAiQUI9W1s6ZGlnaXQ6XV0uW1s6ZGlnaXQ6XV1bWzpkaWdpdDpdXVtbOmRpZ2l0Ol1dIiB8IHNlZCAtcyAncy9BQj0vL2cnID4gQ0xFLkY0LkFCCgpgYGAKCkFsbGVsZSBiYWxhbmNlIGlzIHRoZSByYXRpbyBvZiByZWFkcyBmb3IgcmVmZXJlbmNlIGFsbGVsZSB0byBhbGwgcmVhZHMsIGNvbnNpZGVyaW5nIG9ubHkgcmVhZHMgZnJvbSBpbmRpdmlkdWFscyBjYWxsZWQgYXMgaGV0ZXJvenlnb3VzLiBWYWx1ZXMgcmFuZ2UgZnJvbSAwIC0gMTsgYWxsZWxlIGJhbGFuY2UgKGZvciByZWFsIGxvY2kpIHNob3VsZCBiZSBhcHByb3guIDAuNS4gRmlsdGVyIGNvbnRpZ3MgU05QcyBmb3Igd2hpY2ggdGhlIHdpdGggYWxsZWxlIGJhbGFuY2UgPCAwLjI1IGFuZCA+IDAuNzUuCgpgYGB7ciBwbG90IEFCLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD01fQoKcmVhZC50YWJsZSgiZGF0YS9WQ0YvdGVtcC9DTEUuRjQuQUIiLAogICAgICAgICAgIGNvbC5uYW1lcyA9ICJBQiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gQUIpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjAxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjUsIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMC4yNSwgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAuNzUsIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICB0aGVtZV9zdGFuZGFyZAoKYGBgCgpGaWx0ZXIgY29udGlncyB3aXRoIFNOUCBjYWxscyB3aXRoIEFCID4gMC4yNSwgQUIgPiAwLjc1OyByZXRhaW4gbG9jaSB2ZXJ5IGNsb3NlIHRvIDAgKHJldGFpbiBsb2NpIHRoYXQgYXJlIGZpeGVkIHZhcmlhbnRzKS4gUmVtb3ZlIGdlbm90eXBlcyBpZiB0aGUgcXVhbGl0eSBzdW0gb2YgdGhlIHJlZmVyZW5jZSBvciBhbHRlcm5hdGUgYWxsZWxlIHdhcyAwLgoKYGBge2Jhc2gsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CgojIHRoaXMgd29ya3MuLi4gbm90IHN1cmUgd2h5IGNhbnQgd3JpdGUgdG8gZmlsZSBpbiB0aGUgZm9sZGVyCnZjZmZpbHRlciAtcyAtZiAiQUIgPiAwLjI1ICYgQUIgPCAwLjc1IHwgQUIgPCAwLjAxIHwgQUIgPiAwLjk5IiAtcyAtZyAiUVIgPiAwIHwgUUEgPiAwIiBDTEUuRjMucmVjb2RlLnZjZiA+IENMRS5GNC52Y2YgCgptYXdrICchLyMvJyBDTEUuRjQudmNmIHwgd2MgLWwKCmBgYAoKUmVtYWluaW5nIFNOUHM6IDY1LDgzNy4KCgojIyMgRmlsdGVyIDU6IHF1YWxpdHkvZGVwdGggcmF0aW8KCmBgYHtiYXNofQoKIyBzaXRlIGRlcHRoCmN1dCAtZjggQ0xFLkY0LnZjZiB8IGdyZXAgLW9lICJEUD1bMC05XSoiIHwgc2VkIC1zICdzL0RQPS8vZycgPiBDTEUuNC5ERVBUSAoKIyBxdWFsaXR5IHNjb3JlCm1hd2sgJyEvIy8nICBDTEUuRjQudmNmIHwgY3V0IC1mMSwyLDYgPiBDTEUuRjQubG9jaS5xdWFsCgpgYGAKCkNvbXBhcmUgcXVhbGl0eS9kZXB0aCByYXRpby4KCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTQsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIGRlcHRoCmRlcHRoIDwtIHJlYWQudGFibGUoImRhdGEvVkNGL3RlbXAvQ0xFLjQuREVQVEgiLAogICAgICAgICAgICAgICAgICAgIGNvbC5uYW1lcyA9ICJkZXB0aCIpCgojIHF1YWxpdHkgc2NvcmUKcXVhbCA8LSByZWFkLnRhYmxlKCJkYXRhL1ZDRi90ZW1wL0NMRS5GNC5sb2NpLnF1YWwiLAogICAgICAgICAgICAgICAgICAgY29sLm5hbWVzID0gYygibG9jdXMiLCAicG9zIiwgInF1YWwiKSkKCnRlbXAgPC0gYmluZF9jb2xzKHF1YWwsIGRlcHRoKSAlPiUKICBtdXRhdGUocmF0aW8gPSBxdWFsL2RlcHRoKQoKZ2dwbG90KHRlbXAsIGFlcyh4ID0gcmF0aW8pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjI1LCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZ3JleTg1IikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAuMiwgY29sb3IgPSAiZGFya3JlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgdGhlbWVfc3RhbmRhcmQKCmBgYAoKUmVtb3ZlIGxvY2kgd2l0aCBxdWFsaXR5L2RlcHRoIHJhdGlvIDwgMC4yCgpgYGB7YmFzaH0KCnZjZmZpbHRlciAtcyAtZiAiUVVBTCAvIERQID4gMC4yIiBDTEUuRjQudmNmID4gQ0xFLkY1LnZjZiAKCm1hd2sgJyEvIy8nIENMRS5GNS52Y2YgfCB3YyAtbAoKYGBgCgpSZW1haW5pbmcgU05QczogMzgsMzM0CgojIyMgRmlsdGVyIDY6IG1hcHBpbmcgcXVhbGl0eQoKYGBge2Jhc2ggcXVlcnkgbWFwcGluZyBxdWFsaXR5LCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQoKY3V0IC1mOCBDTEUuRjUudmNmIHwgZ3JlcCAtb2UgIk1RTT1bMC05XSoiIHwgc2VkIC1zICdzL01RTT0vL2cnID4gQ0xFLkY1Lk1RTQoKY3V0IC1mOCBDTEUuRjUudmNmIHwgZ3JlcCAtb2UgIk1RTVI9WzAtOV0qIiB8IHNlZCAtcyAncy9NUU1SPS8vZycgPiBDTEUuRjUuTVFNUgoKYGBgCgpSZW1vdmUgbG9jaSBiYXNlZCBvbiByYXRpbyBvZiBtYXBwaW5nIHF1YWxpdHkgZm9yIHJlZmVyZW5jZSBhbmQgYWx0ZXJuYXRlIGFsbGVsZSwgaS5lLiBzaXRlcyB0aGF0IGhhdmUgYSBoaWdoIGRpc2NyZXBhbmN5IGJldHdlZW4gdGhlIG1hcHBpbmcgcXVhbGl0aWVzIG9mIHR3byBhbGxlbGVzLgoKYGBge3IgcGxvdCBtYXAgcXVhbCByYXRpb3MsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTV9Cgp0ZW1wIDwtIHJlYWQudGFibGUoImRhdGEvVkNGL3RlbXAvQ0xFLkY1Lk1RTSIsIGNvbC5uYW1lcyA9ICJNUU0iKQoKbWFwcXVhbCA8LSByZWFkLnRhYmxlKCJkYXRhL1ZDRi90ZW1wL0NMRS5GNS5NUU1SIiwgY29sLm5hbWVzID0gIk1RTVIiKQoKbWFwcXVhbCA8LSBiaW5kX2NvbHMobWFwcXVhbCwgdGVtcCkgJT4lCiAgbXV0YXRlKHJhdGlvID0gTVFNL01RTVIpCgpmaWx0ZXIgPC0gbWFwcXVhbCAlPiUKICBmaWx0ZXIocmF0aW8gPCAwLjI1IHwgcmF0aW8gPiAxLjc1KQoKZ2dwbG90KG1hcHF1YWwsIGFlcyh4ID0gTVFNLCB5ID0gTVFNUikpICsKICBnZW9tX3BvaW50KHNoYXBlID0gMSwgc2l6ZSA9IDEpICsgCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBzaXplID0gMSwgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gNCwgc2l6ZSA9IDEsIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMC41NzEsIHNpemUgPSAxLCBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBmaWx0ZXIsIGFlcyh4ID0gTVFNLCB5ID0gTVFNUiksIHNoYXBlID0gMjEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJyZWQiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgNjUpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgNjUpKSArCiAgdGhlbWVfc3RhbmRhcmQKCmBgYAoKRmlsdGVyIGxvY2kgd2l0aCBtYXBwaW5nIHF1YWxpdHkgcmF0aW8gPCAwLjI1IGFuZCA+IDEuNzUuCgpgYGB7YmFzaCBmaWx0ZXIgbWFwIHF1YWwgcmF0aW99Cgp2Y2ZmaWx0ZXIgLXMgLWYgIk1RTSAvIE1RTVIgPiAwLjI1ICYgTVFNIC8gTVFNUiA8IDEuNzUiIENMRS5GNS52Y2YgPiBDTEUuRjYudmNmCgptYXdrICchLyMvJyBDTEUuRjYudmNmIHwgd2MgLWwKCmBgYAoKUmVtYWluaW5nIFNOUHM6IDM2LDYzMy4KCiMjIyBGaWx0ZXIgNzogU3RyYW5kIGJhbGFuY2UKClNSRjogTnVtYmVyIG9mIHJlZmVyZW5jZSBvYnNlcnZhdGlvbnMgb24gdGhlIGZvcndhcmQgc3RyYW5kClNBRjogTnVtYmVyIG9mIGFsdGVybmF0ZSBvYnNlcnZhdGlvbnMgb24gdGhlIGZvcndhcmQgc3RyYW5kClNSUjogTnVtYmVyIG9mIHJlZmVyZW5jZSBvYnNlcnZhdGlvbnMgb24gdGhlIHJldmVyc2Ugc3RyYW5kClNBUjogTnVtYmVyIG9mIGFsdGVybmF0ZSBvYnNlcnZhdGlvbnMgb24gdGhlIHJldmVyc2Ugc3RyYW5kCgpgYGB7YmFzaCBxdWVyeSBzdHJhbmRlZG5lc3MsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CgpjdXQgLWY4IENMRS5GNi52Y2YgfCBncmVwIC1vZSAiU0FGPVswLTldKiIgfCBzZWQgLXMgJ3MvU0FGPS8vZycgPiBDTEUuRjYuU0FGCgpjdXQgLWY4IENMRS5GNi52Y2YgfCBncmVwIC1vZSAiU0FSPVswLTldKiIgfCBzZWQgLXMgJ3MvU0FSPS8vZycgPiBDTEUuRjYuU0FSCgpjdXQgLWY4IENMRS5GNi52Y2YgfCBncmVwIC1vZSAiU1JGPVswLTldKiIgfCBzZWQgLXMgJ3MvU1JGPS8vZycgPiBDTEUuRjYuU1JGCgpjdXQgLWY4IENMRS5GNi52Y2YgfCBncmVwIC1vZSAiU1JSPVswLTldKiIgfCBzZWQgLXMgJ3MvU1JSPS8vZycgPiBDTEUuRjYuU1JSCgpgYGAKClBhaXJlZCBlbmQgcmVhZHMgc2hvdWxkIG5vdCBvdmVybGFwLCBhbmQgYSBTTlAgc2l0ZSBzaG91bGQgb25seSBiZSBjb3ZlcmVkIGJ5IGVpdGhlciB0aGUgZm9yd2FyZCBvciByZXZlcnNlIHJlYWQgKHN0cmFuZCkuIAoKYGBge3IgcGxvdCBzdHJhbmRlZG5lc3MsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTV9CgpTQUYgPC0gcmVhZC50YWJsZSgiZGF0YS9WQ0YvdGVtcC9DTEUuRjYuU0FGIiwKICAgICAgICAgICAgICAgICAgY29sLm5hbWVzID0gIlNBRiIpCgpTQVIgPC0gcmVhZC50YWJsZSgiZGF0YS9WQ0YvdGVtcC9DTEUuRjYuU0FSIiwKICAgICAgICAgICAgICAgICAgY29sLm5hbWVzID0gIlNBUiIpCgpzdHJhbmRzIDwtIGJpbmRfY29scyhTQUYsIFNBUikKClNSRiA8LSByZWFkLnRhYmxlKCJkYXRhL1ZDRi90ZW1wL0NMRS5GNi5TUkYiLAogICAgICAgICAgICAgICAgICBjb2wubmFtZXMgPSAiU1JGIikKCnN0cmFuZHMgPC0gYmluZF9jb2xzKHN0cmFuZHMsIFNSRikKClNSUiA8LSByZWFkLnRhYmxlKCJkYXRhL1ZDRi90ZW1wL0NMRS5GNi5TUlIiLAogICAgICAgICAgICAgICAgICBjb2wubmFtZXMgPSAiU1JSIikKCnN0cmFuZHMgPC0gYmluZF9jb2xzKHN0cmFuZHMsIFNSUikgJT4lCiAgbXV0YXRlKHJhdGlvQSA9IFNBRi9TQVIsIHJhdGlvUiA9IFNSRi9TUlIpCgpnZ3Bsb3Qoc3RyYW5kcywgYWVzKHggPSBTQUYsIHkgPSBTQVIpKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDEpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDAuMSwgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMTAwLCBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgdGhlbWVfc3RhbmRhcmQKCmBgYAoKUmVtb3ZlIFNOUCBzaXRlcyB0aGF0IGhhdmUgPiAxMDB4IG1vcmUgZm9yd2FyZCBhbHRlcm5hdGUgcmVhZHMgdGhhbiByZXZlcnNlIGFsdGVybmF0ZSByZWFkcyBhbmQgPiAxMDB4IG1vcmUgZm9yd2FyZCByZXZlcnNlIHJlYWRzIHRoYW4gcmV2ZXJzZSBhbHRlcm5hdGUgcmVhZHMuCgpgYGB7YmFzaCBmaWx0ZXIgc3RyYW5kZWRuZXNzfQoKdmNmZmlsdGVyIC1mICJTQUYgLyBTQVIgPiAxMDAgJiBTUkYgLyBTUlIgPiAxMDAgfCBTQVIgLyBTQUYgPiAxMDAgJiBTUlIgLyBTUkYgPiAxMDAiIC1zIENMRS5GNi52Y2YgPiBDTEUuRjcudmNmCgptYXdrICchLyMvJyBDTEUuRjcudmNmIHwgd2MgLWwKCmBgYAoKTnVtYmVyIG9mIFNOUHMgcmVtYWluaW5nOiAzNCw1ODcuCgojIyMgRmlsdGVyIDg6IFByb3Blcmx5IHBhaXJlZCBzdGF0dXMKClBBSVJFRDogUHJvcG9ydGlvbiBvZiBvYnNlcnZlZCBhbHRlcm5hdGUgYWxsZWxlcyB3aGljaCBhcmUgc3VwcG9ydGVkIGJ5IHByb3Blcmx5IHBhaXJlZCByZWFkIGZyYWdtZW50cwpQQUlSRURSOiBQcm9wb3J0aW9uIG9mIG9ic2VydmVkIHJlZmVyZW5jZSBhbGxlbGVzIHdoaWNoIGFyZSBzdXBwb3J0ZWQgYnkgcHJvcGVybHkgcGFpcmVkIHJlYWQgZnJhZ21lbnRzCgpJZGVudGlmeSBsb2NpIHdpdGggb25seSB1bnBhaXJlZCByZWFkcyBtYXBwaW5nIHRvIHRoZW0gLSBhbiBhcnRpZmFjdCBpbnRyb2R1Y2VkIGR1ZSBkZSBub3ZvIHJlZmVyZW5jZSBhc3NlbWJseS4gQ29tcGFyZSBudW1iZXIgb2YgcGFpcmVkIHJlYWRzIG1hcHBpbmcgdGhlIHJlZmVyZW5jZSBhbmQgdGhlIGFsdGVybmF0ZSBhbGxlbGVzIHRvIGlkZW50aWZ5IGRpc2NyZXBhbmN5IGluIHRoZSBwYWlyZWQgc3RhdHVzIGZvciByZWFkcyBzdXBwb3J0aW5nIHJlZmVyZW5jZSBhbmQgYWx0ZXJuYXRlIGFsbGVsZXMuCgpgYGB7YmFzaCBmaWx0ZXIgcGFpcmVkIHN0YXR1cywgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KCnZjZmZpbHRlciAtZiAiUEFJUkVEID4gMC4wNSAmIFBBSVJFRFIgPiAwLjA1ICYgUEFJUkVEUiAvIFBBSVJFRCA8IDEuNzUgJiBQQUlSRURSIC8gUEFJUkVEID4gMC4yNSB8IFBBSVJFRCA8IDAuMDUgJiBQQUlSRURSIDwgMC4wNSIgLXMgQ0xFLkY3LnZjZiA+IENMRS5GOC52Y2YKCm1hd2sgJyEvIy8nIENMRS5GOC52Y2YgfCB3YyAtbAoKYGBgCgpOdW1iZXIgb2YgU05QcyBpbiBkYXRhIHNldDogMzIsNDQ1LgoKIyMjIEZpbHRlciA5OiBNYXhpbXVtIGRlcHRoICYgUXVhbGl0eQoKSWRlbnRpZnkgZGlzdHJpYnV0aW9uIG9mIGRlcHRoIChiYXNlZCBvbiBvcmlnaW5hbCBkYXRhIHNldCkgdG8gaWRlbnRpZnkgbG9jaSB3aXRoIGV4Y2VzcyBjb3ZlcmFnZS4KCk9yaWdpbmFsIG51bWJlciBvZiBpbmRpdmlkdWFscyBpbiBkYXRhIHNldCBpcyBgciBucm93KGluZF9zdGF0c19yYXcpYCAoSU5GTyBmbGFncyBpbiBmaWx0ZXJlZCBkYXRhIHNldCBhcmUgYXJlIGJhc2VkIG9uIG9yaWdpbmFsIG51bWJlciBvZiBpbmRpdmlkdWFscyBpbiBkYXRhIHNldCkuCgpDcmVhdGUgZmlsZSB3aXRoIHRoZSBvcmlnaW5hbCBzaXRlIGRlcHRoIGFuZCBxdWFsaXR5IHNjb3JlIGZvciBlYWNoIHNpdGU6CgpgYGB7YmFzaCwgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KCiMgc2l0ZSBkZXB0aApjdXQgLWY4IENMRS5GOC52Y2YgfCBncmVwIC1vZSAiRFA9WzAtOV0qIiB8IHNlZCAtcyAncy9EUD0vL2cnID4gQ0xFLkY4LkRFUFRICgojIHF1YWxpdHkgc2NvcmUKbWF3ayAnIS8jLycgIENMRS5GOC52Y2YgfCBjdXQgLWYxLDIsNiA+IENMRS5GOC5sb2NpLnF1YWwKCmBgYAoKQ2FsY3VsYXRlIGF2ZXJhZ2UgZGVwdGggYW5kIHN0YW5kYXJkIGRldmlhdGlvbjoKCmBgYHtyIHBsb3QgZGVwdGggdnMgcXVhbCwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NX0KCiMgZGVwdGgKZGVwdGggPC0gcmVhZC50YWJsZSgiZGF0YS9WQ0YvdGVtcC9DTEUuRjguREVQVEgiLAogICAgICAgICAgICAgICAgICAgIGNvbC5uYW1lcyA9ICJkZXB0aCIpCgojIHF1YWxpdHkgc2NvcmUKcXVhbCA8LSByZWFkLnRhYmxlKCJkYXRhL1ZDRi90ZW1wL0NMRS5GOC5sb2NpLnF1YWwiLAogICAgICAgICAgICAgICAgICAgY29sLm5hbWVzID0gYygibG9jdXMiLCAicG9zIiwgInF1YWwiKSkKCiMgbWVhbiBkZXB0aAptZWFuX2RlcHRoIDwtIG1lYW4oZGVwdGgkZGVwdGgpCgojIHN0YW5kYXJkIGRldmlhdGlvbgpzdGQgPC0gc2QoZGVwdGgkZGVwdGgpCgojIGNhbGN1bGF0ZSBjdXRvZmYKY3V0b2ZmIDwtIHN1bShtZWFuX2RlcHRoICsgKDIqc3RkKSkKCiMgaWRlbnRpZnkgU05QcyB3aXRoIGV4Y2VzcyAoaS5lLiBkZXB0aCA+IG1lYW4gZGVwdGggKyAxIHN0YW5kYXJkIGRldmlhdGlvbgojIGFuZCBxdWFsaXR5IHNjb3JlIDwgMnggdGhlIGRlcHRoIGF0IHRoYXQgc2l0ZQpkZiA8LSBiaW5kX2NvbHMocXVhbCwgZGVwdGgpICU+JQogIG11dGF0ZShxdWFsY3V0b2ZmID0gMipkZXB0aCkKCnJlbW92ZWxvYyA8LSBkZiAlPiUKICBmaWx0ZXIoZGVwdGggPiBjdXRvZmYpICU+JQogIGZpbHRlcihxdWFsIDwgMipkZXB0aCkKCiMgcGxvdApnZ3Bsb3QoZGYsIGFlcyh4ID0gZGVwdGgsIHkgPSBxdWFsKSkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcmVtb3ZlbG9jLCBhZXMoeCA9IGRlcHRoLCB5ID0gcXVhbCksIHNoYXBlID0gMjEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJyZWQiKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBkZiwgYWVzKHggPSBkZXB0aCwgeSA9IHF1YWxjdXRvZmYpLCBjb2xvciA9ICJibHVlIiwgIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gY3V0b2ZmLCBjb2xvciA9ICJibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICB0aGVtZV9zdGFuZGFyZAoKTFEgPC0gcmVtb3ZlbG9jICU+JQogIHNlbGVjdChsb2N1cywgcG9zKQoKd3JpdGUudGFibGUoTFEsICJkYXRhL1ZDRi90ZW1wL0xRX0Y5LmxvY2kiLCAKICAgICAgICAgICAgY29sLm5hbWVzID0gRkFMU0UsIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFKQoKYGBgCgpNZWFuIGRlcHRoIHBlciBsb2N1cyAoYWNyb3NzIGFsbCBpbmRpdnVhbHMpIGlzIGByIG1lYW5fZGVwdGhgIGFuZCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIGlzIGByIHN0ZGAuIAoKRmlsdGVyIFNOUCBzaXRlIHdpdGggZGVwdGggPiBtZWFuIGRlcHRoICsgMSBzdGFuZGFyZCBkZXZpYXRpb24gPSBgciBzdW0obWVhbl9kZXB0aCArIDIqc3RkKWAgYW5kIHRoYXQgaGF2ZSBxdWFsaXR5IHNjb3JlcyA8IDJ4IHRoZSBkZXB0aCBhdCB0aGF0IHNpdGUgYW5kIG91dHB1dCBkZXB0aCBwZXIgc2l0ZS4KCmBgYHtiYXNoIGZpbHRlciBkZXB0aCBxdWFsLCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQoKdmNmdG9vbHMgLS12Y2YgIGRhdGEvVkNGL3RlbXAvQ0xFLkY4LnZjZiAtLWV4Y2x1ZGUtcG9zaXRpb25zIGRhdGEvVkNGL3RlbXAvTFFfRjkubG9jaSAtLXJlY29kZSAtLXJlY29kZS1JTkZPLWFsbCAtLW91dCBkYXRhL1ZDRi90ZW1wL0NMRS5GOWEKCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL0Y0LnZjZiAtLW91dCBkYXRhL1ZDRi90ZW1wL0NMRS5GNCAtLWtlZXAgZGF0YS9WQ0YvZHVwbGljYXRlLmluZCAtLTAxMgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi9GNS52Y2YgLS1vdXQgZGF0YS9WQ0YvdGVtcC9DTEUuRjUgLS1rZWVwIGRhdGEvVkNGL2R1cGxpY2F0ZS5pbmQgLS0wMTIKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvRjYudmNmIC0tb3V0IGRhdGEvVkNGL3RlbXAvQ0xFLkY2IC0ta2VlcCBkYXRhL1ZDRi9kdXBsaWNhdGUuaW5kIC0tMDEyCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL0Y3LnZjZiAtLW91dCBkYXRhL1ZDRi90ZW1wL0NMRS5GNyAtLWtlZXAgZGF0YS9WQ0YvZHVwbGljYXRlLmluZCAtLTAxMgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi9GOC52Y2YgLS1vdXQgZGF0YS9WQ0YvdGVtcC9DTEUuRjggLS1rZWVwIGRhdGEvVkNGL2R1cGxpY2F0ZS5pbmQgLS0wMTIKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvRjlhLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvdGVtcC9DTEUuRjlhIC0ta2VlcCBkYXRhL1ZDRi9kdXBsaWNhdGUuaW5kIC0tMDEyCgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi9GNC52Y2YgLS1vdXQgZGF0YS9WQ0YvdGVtcC9DTEUuRjQgLS1rZWVwIGRhdGEvVkNGL2R1cGxpY2F0ZS5pbmQgLS1nZW5vLWRlcHRoCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL0Y1LnZjZiAtLW91dCBkYXRhL1ZDRi90ZW1wL0NMRS5GNSAtLWtlZXAgZGF0YS9WQ0YvZHVwbGljYXRlLmluZCAtLWdlbm8tZGVwdGgKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvRjYudmNmIC0tb3V0IGRhdGEvVkNGL3RlbXAvQ0xFLkY2IC0ta2VlcCBkYXRhL1ZDRi9kdXBsaWNhdGUuaW5kIC0tZ2Vuby1kZXB0aAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi9GNy52Y2YgLS1vdXQgZGF0YS9WQ0YvdGVtcC9DTEUuRjcgLS1rZWVwIGRhdGEvVkNGL2R1cGxpY2F0ZS5pbmQgLS1nZW5vLWRlcHRoCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL0Y4LnZjZiAtLW91dCBkYXRhL1ZDRi90ZW1wL0NMRS5GOCAtLWtlZXAgZGF0YS9WQ0YvZHVwbGljYXRlLmluZCAtLWdlbm8tZGVwdGgKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvRjlhLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvdGVtcC9DTEUuRjlhIC0ta2VlcCBkYXRhL1ZDRi9kdXBsaWNhdGUuaW5kIC0tZ2Vuby1kZXB0aAoKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjlhLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkY5YSAtLXNpdGUtbWVhbi1kZXB0aAoKYGBgCgpDb21wYXJlIHRoZSBkaXN0cmlidXRpb24gb2YgbWVhbiBkZXB0aCBwZXIgc2l0ZSBhdmVyYWdlZCBhY3Jvc3MgaW5kaXZpZHVhbHMgdG8gZGV0ZXJtaW5lIGN1dC1vZmYgdmFsdWUgb2Ygc2l0ZXMgd2l0aCBleGNlc3NpdmVseSBoaWdoIGRlcHRoIGluZGljYXRpdmUgb2YgcGFyYWxvZ3MvbXVsdGljb3B5IGxvY2kuCgpgYGB7ciBwbG90IGRlcHRoIGRpc3QsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIGNhbGN1bGF0ZSBtZWFuIGRlcHRoIHBlciBzaXRlICgxNzcgaW5kaXZpZHVhbHMpCnJlYWQudGFibGUoImRhdGEvVkNGL0NMRS5GOWEubGRlcHRoLm1lYW4iLAogICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lIAogIGdncGxvdChhZXMoeCA9IE1FQU5fREVQVEgpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAyMCwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oTUVBTl9ERVBUSCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoTUVBTl9ERVBUSCwgLjk1KSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBxdWFudGlsZShNRUFOX0RFUFRILCAuOTkpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgc2NhbGVfeV9zcXJ0KCkgKwogIGxhYnMoeCA9ICJtZWFuIGRlcHRoIHBlciBzaXRlIikgKwogIHRoZW1lX3N0YW5kYXJkCgpgYGAKCkNob29zZSBjdXQtb2ZmIGZvciBtYXhpbXVtIG1lYW4gcmVhZCBkZXB0aCA9IDI1MC4KCmBgYHtiYXNoIGZpbHRlciBtYXggZGVwdGh9Cgp2Y2Z0b29scyAtLXZjZiAgZGF0YS9WQ0YvdGVtcC9DTEUuRjgudmNmIC0tb3V0IGRhdGEvVkNGL3RlbXAvQ0xFLkY5IC0tbWF4LW1lYW5EUCAxODAgLS1leGNsdWRlLXBvc2l0aW9ucyBkYXRhL1ZDRi90ZW1wL0xRX0Y5LmxvY2kgLS1yZWNvZGUgLS1yZWNvZGUtSU5GTy1hbGwgCgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GOS5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0Y5IC0ta2VlcCBkYXRhL1ZDRi9kdXBsaWNhdGUuaW5kIC0tMDEyCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkY5LnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvRjkgLS1rZWVwIGRhdGEvVkNGL2R1cGxpY2F0ZS5pbmQgLS1nZW5vLWRlcHRoCgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GOS5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GOSAtLWRlcHRoCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkY5LnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkY5IC0tc2l0ZS1tZWFuLWRlcHRoCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkY5LnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkY5IC0tbWlzc2luZy1pbmR2CnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkY5LnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkY5IC0tbWlzc2luZy1zaXRlCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkY5LnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkY5IC0taGV0CgpgYGAKCkRlcHRoIGRpc3RyaWJ1dGlvbiBwZXIgbG9jdXMgYWZ0ZXIgZmlsdGVyaW5nOgoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCnJlYWRfdGFibGUyKCJkYXRhL1ZDRi9DTEUuRjkubGRlcHRoLm1lYW4iKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBNRUFOX0RFUFRIKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMTAsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIHRoZW1lX3N0YW5kYXJkCgpgYGAKCkFuYWx5emUgc3RhdHMgcG9zdC1maWx0ZXJpbmc6CgpgYGB7ciBzdGF0cyBGOSwgZmlnLmhlaWdodD0yMCwgZmlnLndpZHRoPTEwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKIyBsb2FkIHN0YXRzIGZpbGVzIC0tLS0KaW5kX3N0YXRzX0Y5IDwtIHJlYWQuaW5kLnN0YXRzKGRpciA9ICJkYXRhL1ZDRiIsIHZjZiA9ICJDTEUuRjkiKSAlPiUKICBzZXBhcmF0ZShJTkRWLCBpbnRvID0gYygiU1AiLCAiTElCIiwgIlNBTVBMRV9JRCIpLCBzZXAgPSAiXyIsIHJlbW92ZSA9IEZBTFNFKQoKbG9jX3N0YXRzX0Y5IDwtIHJlYWQubG9jLnN0YXRzKGRpciA9ICJkYXRhL1ZDRi8iLCB2Y2YgPSAiQ0xFLkY5IikKCmBgYAoKRGF0YSBzZXQgY29udGFpbnMgYHIgbnJvdyhsb2Nfc3RhdHNfRjkpYCBTTlAgc2l0ZXMgYW5kIGByIG5yb3coaW5kX3N0YXRzX0Y5KWAgaW5kaXZpZHVhbHMuCgojIyMgRmlsdGVyIDEwOiBSZW1vdmUgSW5kZWxzCgpSZW1vdmUgSW5kZWxzIGZyb20gZGF0YSBzZXQuCgpgYGB7YmFzaCByZW1vdmUgaW5kZWxzfQoKdmNmYWxsZWxpY3ByaW1pdGl2ZXMgZGF0YS9WQ0YvdGVtcC9DTEUuRjkucmVjb2RlLnZjZiAtLWtlZXAtaW5mbyAtLWtlZXAtZ2VubyA+IGRhdGEvVkNGL3RlbXAvQ0xFLnByaW0udmNmCgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5wcmltLnZjZiAtLW91dCBkYXRhL1ZDRi90ZW1wL0NMRS5GMTAgLS1yZW1vdmUtaW5kZWxzIC0tcmVjb2RlIC0tcmVjb2RlLUlORk8tYWxsCgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMTAucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9GMTAgLS1rZWVwIGRhdGEvVkNGL2R1cGxpY2F0ZS5pbmQgLS0wMTIKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjEwLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvRjEwIC0ta2VlcCBkYXRhL1ZDRi9kdXBsaWNhdGUuaW5kIC0tZ2Vuby1kZXB0aAoKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjEwLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxMCAtLWRlcHRoCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxMC5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMTAgLS1zaXRlLW1lYW4tZGVwdGgKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjEwLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxMCAtLW1pc3NpbmctaW5kdgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMTAucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuRjEwIC0tbWlzc2luZy1zaXRlCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxMC5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMTAgLS1oZXQKCmBgYAoKQW5hbHl6ZSBzdGF0cyBwb3N0LWZpbHRlcmluZzoKCmBgYHtyIHN0YXRzIEYxMCwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTEwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKIyBsb2FkIHN0YXRzIGZpbGVzIC0tLS0KaW5kX3N0YXRzX0YxMCA8LSByZWFkLmluZC5zdGF0cyhkaXIgPSAiZGF0YS9WQ0YiLCB2Y2YgPSAiQ0xFLkYxMCIpICU+JQogIHNlcGFyYXRlKElORFYsIGludG8gPSBjKCJTUCIsICJMSUIiLCAiU0FNUExFX0lEIiksIHNlcCA9ICJfIiwgcmVtb3ZlID0gRkFMU0UpCgpsb2Nfc3RhdHNfRjEwIDwtIHJlYWQubG9jLnN0YXRzKGRpciA9ICJkYXRhL1ZDRi8iLCB2Y2YgPSAiQ0xFLkYxMCIpCgojIHBsb3QgbWlzc2luZyBkYXRhIHBlciBpbmR2IC0tLS0KcDEgPC0gZ2dwbG90KGluZF9zdGF0c19GMTAsIGFlcyh4ID0gTUlTU19DTEUuRjEwKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gLjAxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihNSVNTX0NMRS5GMTAsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMC4yNSksCiAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIm1pc3NpbmcgZGF0YSBwZXIgaW5kdiIpICsKICB0aGVtZV9zdGFuZGFyZAoKIyBwbG90IHJlYWQgZGVwdGggcGVyIGluZHYgLS0tLQpwMiA8LSBnZ3Bsb3QoaW5kX3N0YXRzX0YxMCwgYWVzKHggPSBNRUFOX0RFUFRIX0NMRS5GMTApKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMCwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oTUVBTl9ERVBUSF9DTEUuRjEwLCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IDIwKSwKICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAibWVhbiByZWFkIGRlcHRoIHBlciBpbmR2IikgKwogIHRoZW1lX3N0YW5kYXJkCgojIHBsb3QgZGVwdGggdnMgbWlzc2luZyAtLS0tCnAzIDwtIGdncGxvdChpbmRfc3RhdHNfRjEwLCBhZXMoeCA9IE1FQU5fREVQVEhfQ0xFLkYxMCwgeSA9IE1JU1NfQ0xFLkYxMCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKE1FQU5fREVQVEhfQ0xFLkYxMCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAyMCksCiAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1lYW4oTUlTU19DTEUuRjEwLCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IDAuMjUpLAogICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJtZWFuIGRlcHRoIHBlciBpbmR2IiwgeSA9ICIlIG1pc3NpbmcgZGF0YSIpICsKICB0aGVtZV9zdGFuZGFyZAoKIyBwbG90IGRpc3RyaWJ1dGlvbiBtaXNzaW5nIGRhdGEgcGVyIGxvY3VzIC0tLS0KcDQgPC0gZ2dwbG90KGxvY19zdGF0c19GMTAsIGFlcyh4ID0gTUlTU19DTEUuRjEwKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4wMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oTUlTU19DTEUuRjEwLCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IDAuMSksCiAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIiUgbWlzc2luZyBkYXRhIHBlciBsb2N1cyIpICsKICB0aGVtZV9zdGFuZGFyZAoKIyBwbG90IGRpc3RyaWJ1dGlvbiBtZWFuIHJlYWQgZGVwdGggLS0tLQpwNSA8LSBnZ3Bsb3QobG9jX3N0YXRzX0YxMCwgYWVzKHggPSBNRUFOX0RFUFRIX0NMRS5GMTApKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1LCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihNRUFOX0RFUFRIX0NMRS5GMTAsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMjApLAogICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJtZWFuIHJlYWQgZGVwdGggcGVyIGxvY3VzIikgKwogIHRoZW1lX3N0YW5kYXJkCgojIHBsb3QgcmVhZCBkZXB0aCB2cyBtaXNzaW5nIGRhdGEgLS0tLQpwNiA8LSBnZ3Bsb3QobG9jX3N0YXRzX0YxMCwgYWVzKHggPSBNRUFOX0RFUFRIX0NMRS5GMTAsIHkgPSBNSVNTX0NMRS5GMTApKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihNRUFOX0RFUFRIX0NMRS5GMTAsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMjApLAogICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtZWFuKE1JU1NfQ0xFLkYxMCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSAwLjEpLAogICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJtZWFuIGRlcHRoIHBlciBsb2N1cyIsIHkgPSAiJSBtaXNzaW5nIGRhdGEiKSArCiAgdGhlbWVfc3RhbmRhcmQKCm0zIDwtIG11bHRpcGxvdChwMSwgcDIsIHAzLCBwNCwgcDUsIHA2LCBjb2xzPTIpCgpgYGAKCkRhdGEgc2V0IGNvbnRhaW5zIGByIG5yb3cobG9jX3N0YXRzX0YxMClgIFNOUCBzaXRlcyBhbmQgYHIgbnJvdyhpbmRfc3RhdHNfRjEwKWAgaW5kaXZpZHVhbHMuCgojIyMgRmlsdGVyIDExOiBNaXNzaW5nIGRhdGEgYW5kIG1pbmltdW0gZGVwdGggcGVyIGxvY3VzCgpBc3Nlc3MgaW5kaXZpZHVhbHMgaW4gdGVybXMgb2YgbG93IGNvdmVyYWdlLCBoaWdoIG1pc3NpbmcgZGF0YSwgYW5kIGZyZXF1ZW5jeSBidXJkZW4gKGkuZS4gZGlzdHJpYnV0aW9uIG9mIHRoZSBudW1iZXIgb2YgdmFyaWFudHMgd2l0aGluIGVhY2ggaW5kaXZpZHVhbCBvZiBhIHNwZWNpZmljIGZyZXF1ZW5jeSkuCgpgYGB7YmFzaCBnZW5vIDkwIG1lYW5EUCAxNX0KCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxMC5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL3RlbXAvQ0xFLkYxMSAtLW1pbi1tZWFuRFAgMTUgLS1tYXgtbWlzc2luZyAwLjkgLS1yZWNvZGUgLS1yZWNvZGUtSU5GTy1hbGwKCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxMS5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0YxMSAtLWtlZXAgZGF0YS9WQ0YvZHVwbGljYXRlLmluZCAtLTAxMgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMTEucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9GMTEgLS1rZWVwIGRhdGEvVkNGL2R1cGxpY2F0ZS5pbmQgLS1nZW5vLWRlcHRoCgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMTEucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuRjExIC0tZGVwdGgKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjExLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxMSAtLXNpdGUtbWVhbi1kZXB0aAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMTEucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuRjExIC0tbWlzc2luZy1pbmR2CnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxMS5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMTEgLS1taXNzaW5nLXNpdGUKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjExLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxMSAtLWhldAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMTEucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuRjExIC0taGFyZHkKCmBgYAoKIyMjIEZpbHRlciAxMjogRXhjZXNzIGhldGVyb3p5Z29zaXR5CgpJZGVudGlmeSBTTlBzIHdpdGggbW9yZSB0aGFuIDAuNSBoZXRlcm96eWdvc2l0eSBhbmQgc2lnbmlmaWNhbnQuCgpgYGB7cn0KCiMgY2FsY3VsYXRlIG9ic2VydmVkIGhldGVyb3p5Z29zaXR5Cmh3ZSA8LSByZWFkLnRhYmxlKCJkYXRhL1ZDRi9DTEUuRjExLmh3ZSIsIAogICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIGhlYWRlciA9IFRSVUUpICU+JQogIHNlbGVjdCgtQ2hpU3FfSFdFKSAlPiUKICBzZXBhcmF0ZShgT0JTLkhPTTEuSEVULkhPTTIuYCwgCiAgICAgICAgICAgaW50byA9IGMoIm9ic19ob20xIiwgIm9ic19oZXQiLCAib2JzX2hvbTIiKSwgCiAgICAgICAgICAgc2VwID0gIi8iLCBjb252ZXJ0ID0gVFJVRSkgJT4lCiAgc2VwYXJhdGUoYEUuSE9NMS5IRVQuSE9NMi5gLCAKICAgICAgICAgICBpbnRvID0gYygiZXhwX2hvbTEiLCAiZXhwX2hldCIsICJleHBfaG9tMiIpLCAKICAgICAgICAgICBzZXAgPSAiLyIsIGNvbnZlcnQgPSBUUlVFKSAlPiUgICAKICBtdXRhdGUoSG8gPSBvYnNfaGV0LyhvYnNfaG9tMSArIG9ic19ob20yICsgb2JzX2hldCksCiAgICAgICAgIEhlID0gZXhwX2hldC8oZXhwX2hvbTEgKyBleHBfaG9tMiArIGV4cF9oZXQpKSAlPiUKICBzZWxlY3QoQ0hSLCBQT1MsIG9ic19ob20xLCBleHBfaG9tMSwgb2JzX2hvbTIsIGV4cF9ob20yLCBQX0hFVF9ERUZJQ0lULCBvYnNfaGV0LCBleHBfaGV0LCBQX0hFVF9FWENFU1MsIEhvLCBIZSwgUF9IV0UpCgpgYGAKCkRpc3RyaWJ1dGlvbiBvZiBvYnNlcnZlZCBoZXRlcm96eWdvc2l0eS4KCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTR9CgpnZ3Bsb3QoaHdlLCBhZXMoeCA9IEhvKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMDUsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAuNSwgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJvYnNlcnZlZCBoZXRlcm96eWdvc2l0eSIpICsKICB0aGVtZV9zdGFuZGFyZAoKYGBgCgpJZGVudGlmeSBsb2NpIHdpdGggaGV0ZXJvenlnb3NpdHkgPiAwLjUsIHRoZW4gY29ycmVjdCBwLXZhbHVlcyBmb3IgbXVsdGlwbGUgY29tcGFyaXNvbnMgdXNpbmcgQmVuamFtaW5pLUhvY2hiZXJnIG1ldGhvZC4KCmBgYHtyfQoKIyBpZGVudGlmeSBjb250aWdzIHdpdGggU05QcyB3aXRoIEhvID4gMC41ICYgc2lnbmlmaWNhbnQKZXhjZXNzX2h3ZSA8LSBod2UgJT4lCiAgZmlsdGVyKEhvID4gMC41KSAlPiUKICBtdXRhdGUocF9hZGogPSBwLmFkanVzdChQX0hFVF9FWENFU1MpLCBtZXRob2QgPSAiZmRyIikgJT4lCiAgZmlsdGVyKHBfYWRqIDwgMC4wMSkKCmNvbnRpZ3MgPC0gaHdlICU+JQogIGZpbHRlcihDSFIgJWluJSBleGNlc3NfaHdlJENIUikgJT4lCiAgc2VsZWN0KENIUikgJT4lCiAgdW5pcXVlKCkKCiMgaWRlbnRpZnkgYWxsIFNOUHMgb24gY29udGlncwpTTlBzIDwtIGh3ZSAlPiUKICBmaWx0ZXIoQ0hSICVpbiUgY29udGlncyRDSFIpICU+JQogIHNlbGVjdChDSFIsIFBPUykKCiMgVmlldyhjb250aWdzKQoKIyBXcml0ZSBjb250aWcvcG9zaXRpb24gdG8gdGV4dCBmaWxlLCB1c2UgZmlsZSB3aXRoIHZjZnRvb2xzIHRvIHJlbW92ZSBwb3NpdGlvbnMgZnJvbSBkYXRhc2V0IAp3cml0ZS50YWJsZShTTlBzLCBmaWxlID0gImRhdGEvVkNGL2hldGV4Y2Vzcy5sb2NpIiwgCiAgICAgICAgICAgIGNvbC5uYW1lcz0gRkFMU0UsIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFKQoKYGBgCgpSZW1vdmUgU05QcyB3aXRoIGV4Y2VzcyBoZXRlcm96eWdvc2l0eSBhbmQgY29udGlncyB3aXRoIG1vcmUgdGhhbiBvbmUgU05QIHcvZXhjZXNzIGhldGVyb3p5Z29zaXR5LgoKYGBge2Jhc2h9Cgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMTEucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi90ZW1wL0NMRS5GMTIgLS1leGNsdWRlLXBvc2l0aW9ucyBkYXRhL1ZDRi9oZXRleGNlc3MubG9jaSAtLXJlY29kZSAtLXJlY29kZS1JTkZPLWFsbAoKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjEyLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvRjEyIC0ta2VlcCBkYXRhL1ZDRi9kdXBsaWNhdGUuaW5kIC0tMDEyCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxMi5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0YxMiAtLWtlZXAgZGF0YS9WQ0YvZHVwbGljYXRlLmluZCAtLWdlbm8tZGVwdGgKCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxMi5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMTIgLS1kZXB0aAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMTIucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuRjEyIC0tc2l0ZS1tZWFuLWRlcHRoCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxMi5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMTIgLS1taXNzaW5nLWluZHYKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjEyLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxMiAtLW1pc3Npbmctc2l0ZQp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMTIucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuRjEyIC0taGV0CnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxMi5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMTIgLS1nZW5vLWRlcHRoCgpgYGAKClZpc3VhbGl6ZSBzdGF0czoKCmBgYHtyIHN0YXRzIEYxMiwgZmlnLmhlaWdodD0yMCwgZmlnLndpZHRoPTEwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKIyBsb2FkIHN0YXRzIGZpbGVzIC0tLS0KaW5kX3N0YXRzX0YxMiA8LSByZWFkLmluZC5zdGF0cyhkaXIgPSAiZGF0YS9WQ0YiLCB2Y2YgPSAiQ0xFLkYxMiIpICU+JQogIHNlcGFyYXRlKElORFYsIGludG8gPSBjKCJTUCIsICJMSUIiLCAiU0FNUExFX0lEIiksIHNlcCA9ICJfIiwgcmVtb3ZlID0gRkFMU0UpCgpsb2Nfc3RhdHNfRjEyIDwtIHJlYWQubG9jLnN0YXRzKGRpciA9ICJkYXRhL1ZDRi8iLCB2Y2YgPSAiQ0xFLkYxMiIpCgpgYGAKCkRhdGEgc2V0IGNvbnRhaW5zIGByIG5yb3cobG9jX3N0YXRzX0YxMilgIFNOUCBzaXRlcyBhbmQgYHIgbnJvdyhpbmRfc3RhdHNfRjEyKWAgaW5kaXZpZHVhbHMuCgojIyMgRmlsdGVyIDEzOiBGaWx0ZXIgTFEgaW5kaXZpZHVhbHMKCkNvbXBhcmUgbWVhbiBkZXB0aCBhbmQgbnVtYmVyIG9mIHNpdGVzIGNhbGxlZCBwZXIgaW5kaXZpZHVhbC4KCmBgYHtyIGlkZXB0aCBzaXRlcyB2cyBkZXB0aCwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCnJlYWRfdGFibGUyKCJkYXRhL1ZDRi9DTEUuRjEyLmlkZXB0aCIpICU+JQogIGdncGxvdChhZXMoeCA9IE5fU0lURVMsIHkgPSBNRUFOX0RFUFRIKSkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJudW1iZXIgb2Ygc2l0ZXMiLCB5ID0gIm1lYW4gcmVhZCBkZXB0aCIpICsKICB0aGVtZV9zdGFuZGFyZAoKYGBgCgpVc2UgZ2Vub3R5cGUgZGVwdGggZmlsZSB0byBpZGVudGlmeSBpbmRpdmlkdWFscyB3aXRoIGhpZ2ggdmFyaWFuY2UgaW4gcmVhZCBkZXB0aCBhY3Jvc3MgbG9jaS4KCmBgYHtyIHZhcmlhbmNlIGRlcHRoIGluZGl2aWR1YWx9CgojIG51bWJlciBvZiBpbmRpdmlkdWFscwpuIDwtIG5yb3coaW5kX3N0YXRzX0YxMikrMQoKIyByZWFkIGdlbm90eXBlIGRlcHRoICYgY29kZSB2YWx1ZXMgPCA1IGFzIG1pc3NpbmcsIHJldGFpbiBvbmx5IG9uZSBsb2N1cyBwZXIgY29udGlnCmdkZXB0aCA8LSByZWFkX3RhYmxlMigiZGF0YS9WQ0YvQ0xFLkYxMi5nZGVwdGgiKSAlPiUKICBzZWxlY3QoLVBPUykgJT4lCiAgZGlzdGluY3QoQ0hST00sIC5rZWVwX2FsbCA9IFRSVUUpICU+JQogIGdhdGhlcihrZXkgPSBJTkRWLCB2YWx1ZSA9IEdERVBUSCwgMjpuKSAlPiUKICBtdXRhdGUoR0RFUFRIID0gYXMubnVtZXJpYyhnc3ViKC0xLCAwLCBHREVQVEgpKSkgJT4lCiAgbXV0YXRlKERFUFRIID0gR0RFUFRIKSAlPiUKICBtdXRhdGUoREVQVEggPSBhcy5udW1lcmljKGdzdWIoIlxcPDFcXD4iLCAwLCBERVBUSCkpKSAlPiUKICBtdXRhdGUoREVQVEggPSBhcy5udW1lcmljKGdzdWIoIlxcPDJcXD4iLCAwLCBERVBUSCkpKSAlPiUKICBtdXRhdGUoREVQVEggPSBhcy5udW1lcmljKGdzdWIoIlxcPDNcXD4iLCAwLCBERVBUSCkpKSAlPiUKICBtdXRhdGUoREVQVEggPSBhcy5udW1lcmljKGdzdWIoIlxcPDRcXD4iLCAwLCBERVBUSCkpKSAlPiUKICBtdXRhdGUoTUlTU0lORyA9IGlmZWxzZShERVBUSCA9PSAwLCAibWlzc2luZyIsICJnZW5vdHlwZWQiKSkgJT4lCiAgbXV0YXRlKEdERVBUSEJJTiA9IGN1dChHREVQVEgsIAogICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygwLCA1LCAxMCwgMjUsIDUwLCAxMDAsIDUwMCwgbWF4KEdERVBUSCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiPDUiLCAiMy0xMCIsICIxMC0yNSIsICIyNS01MCIsICI1MC0xMDAiLCAiMTAwLTUwMCIsICI+NTAwIikpKSAlPiUKICBzZXBhcmF0ZShJTkRWLCBpbnRvID0gYygiU1AiLCAiTElCIiwgIlNBTVBMRV9JRCIpLCBzZXAgPSAiXyIsIHJlbW92ZSA9IEZBTFNFLCBleHRyYSA9ICJtZXJnZSIpICU+JQogIG11dGF0ZShMSUIgPSBzdHJfcmVwbGFjZV9hbGwoTElCLCAiMkEuKiIsICJDTEUyIiksCiAgICAgICAgIExJQiA9IHN0cl9yZXBsYWNlX2FsbChMSUIsICIyQi4qIiwgIkNMRTIiKSwKICAgICAgICAgTElCID0gc3RyX3JlcGxhY2VfYWxsKExJQiwgIjNBLioiLCAiQ0xFMyIpLAogICAgICAgICBMSUIgPSBzdHJfcmVwbGFjZV9hbGwoTElCLCAiM0IuKiIsICJDTEUzIikpCgpnZGVwdGggPC0gbGVmdF9qb2luKGdkZXB0aCwgU2FtcGxlSW5mbywgYnkgPSAiU0FNUExFX0lEIikKCmBgYAoKQ29tcGFyZSBkaXN0cmlidXRpb24gb2YgZGVwdGguCgpgYGB7ciBnZW5vdHlwZSBkZXB0aCBoZWF0bWFwIHBvcCwgZmlnLmhlaWdodD0yMCwgZmlnLndpZHRoPTIwfQoKIyB3b3VsZCBuZWVkIHRvIGpvaW4gd2l0aCBTYW1wbGUgSW5mbyB0byBnZXQgcG9wIHNwZWNpZmljYXRpb25zCmdncGxvdChnZGVwdGgsIGFlcyh4ID0gSU5EViwgeSA9IENIUk9NKSkgKwogIGdlb21fdGlsZShhZXMoZmlsbCA9IEdERVBUSEJJTikpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlyZWN0aW9uID0gLTEsIG9wdGlvbiA9ICJ2aXJpZGlzIiwKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICdnZW5vdHlwZSBkZXB0aCcsCiAgICAgICAgICAgICAgICAgICAgIGRpc2NyZXRlID0gVFJVRSkgKwogIGZhY2V0X2dyaWQoLiB+IFBPUCwgc3BhY2UgPSAiZnJlZSIsIHNjYWxlcyA9ICJmcmVlIiwgZHJvcCA9IFRSVUUpICsKICBsYWJzKHggPSAiaW5kaXZpZHVhbCIsIHkgPSAibG9jdXMiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCmBgYAoKQ29tcGFyZSBkaXN0cmlidXRpb24gb2YgZGVwdGggb2YgaW5kaXZpZHVhbHMgZ3JvdXBlZCBieSBsaWJyYXJ5LgoKYGBge3IgZ2Vub3R5cGUgZGVwdGggaGVhdG1hcCBsaWIsIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD0yMH0KCmdncGxvdChnZGVwdGgsIGFlcyh4ID0gSU5EViwgeSA9IENIUk9NKSkgKyAKICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBHREVQVEhCSU4pKSArIAogIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXJlY3Rpb24gPSAtMSwgb3B0aW9uID0gInZpcmlkaXMiLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ2dlbm90eXBlIGRlcHRoJywKICAgICAgICAgICAgICAgICAgICAgZGlzY3JldGUgPSBUUlVFKSArCiAgZmFjZXRfZ3JpZCguIH4gTElCLCBzcGFjZSA9ICJmcmVlIiwgc2NhbGVzID0gImZyZWUiLCBkcm9wID0gVFJVRSkgKwogIGxhYnMoeCA9ICJpbmRpdmlkdWFsIiwgeSA9ICJsb2N1cyIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKYGBgCgpDb21wYXJlIGRpc3RyaWJ1dGlvbiBvZiBnZW5vdHlwZSBkZXB0aHMgKGFjcm9zcyBhbGwgaW5kaXZpZHVhbHMpLgoKYGBge3IgZGlzdHJpYnV0aW9uIGdlbm90eXBlIGRlcHRocywgZmlnLmhlaWdodD0zLjUsIGZpZy53aWR0aD01LjV9Cgp0ZW1wIDwtIGNvdW50KGdkZXB0aCwgR0RFUFRIQklOKQoKZ2dwbG90KHRlbXAsIGFlcyh4ID0gR0RFUFRIQklOLCB5ID0gbikpICsKICBnZW9tX2JhcihzdGF0PSAiaWRlbnRpdHkiLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICB0aGVtZV9zdGFuZGFyZAoKYGBgCgpJZGVudGlmeSB2YXJpYW5jZSBpbiBkZXB0aCB3L2luIGluZGl2aWR1YWxzLgoKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD05fQoKIyBjYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzCmlkZXB0aCA8LSBnZGVwdGggJT4lCiAgZ3JvdXBfYnkoSU5EVikgJT4lCiAgc3VtbWFyaXplKFRPVEFMID0gc3VtKERFUFRIKSwKICAgICAgICAgICAgTUFYID0gbWF4KERFUFRIKSwKICAgICAgICAgICAgTUVBTiA9IG1lYW4oREVQVEgpLAogICAgICAgICAgICBNRURJQU4gPSBtZWRpYW4oREVQVEgpLAogICAgICAgICAgICBWQVIgPSB2YXIoREVQVEgpLAogICAgICAgICAgICBTVEQgPSBzZChERVBUSCkpICU+JQogIG11dGF0ZShDT0VGRl9WQVIgPSBTVEQvTUVBTiwKICAgICAgICAgUkFUSU9fTUVBTl9NRURJQU4gPSBNRUFOL01FRElBTikKCnAxIDwtIGdncGxvdChpZGVwdGgsIGFlcyh4ID0gTUVBTiwgeSA9IE1FRElBTikpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAiYXV0byIsbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEsIGNvbG9yID0gImRhcmtyZWQiKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDEpICsKICB0aGVtZV9zdGFuZGFyZAoKcDIgPC0gZ2dwbG90KGlkZXB0aCwgYWVzKHggPSBNQVgsIHkgPSBUT1RBTCkpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxLCBjb2xvciA9ICJkYXJrcmVkIikgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxKSArCiAgdGhlbWVfc3RhbmRhcmQKCnAzIDwtIGdncGxvdChpZGVwdGgsIGFlcyh4ID0gVE9UQUwsIHkgPSBWQVIpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImF1dG8iLGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxLCBjb2xvciA9ICJkYXJrcmVkIikgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxKSArCiAgdGhlbWVfc3RhbmRhcmQKCnA0IDwtIGdncGxvdChpZGVwdGgsIGFlcyhNRURJQU4pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMCwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgdGhlbWVfc3RhbmRhcmQKCnA1IDwtIGdncGxvdChpZGVwdGgsIGFlcyhSQVRJT19NRUFOX01FRElBTikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgdGhlbWVfc3RhbmRhcmQKCnA2IDwtIGdncGxvdChpZGVwdGgsIGFlcyhDT0VGRl9WQVIpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjA1LCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICB0aGVtZV9zdGFuZGFyZAoKbTE0YiA8LSBtdWx0aXBsb3QocDEsIHAyLCBwMywgcDQsIHA1LCBwNiwgY29scz0yKQoKYGBgCgpDb21wYXJlIGRlcHRoLCBtaXNzaW5nIGRhdGEgYW5kIGluZGl2aWR1YWwgaGV0ZXJvenlnb3NpdHkgbGV2ZWxzLgoKYGBge3IgaXN0YXRzLCBmaWcuaGVpZ2h0PTE4LCBmaWcud2lkdGg9MTAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgppbWlzcyA8LSByZWFkX3RhYmxlMigiZGF0YS9WQ0YvQ0xFLkYxMi5pbWlzcyIpICU+JQogIHNlbGVjdChJTkRWLCBOX0RBVEEsIEZfTUlTUykKCmlzdGF0cyA8LSBsZWZ0X2pvaW4oaWRlcHRoLCBpbWlzcykKCkZpcyA8LSByZWFkX3RhYmxlMigiZGF0YS9WQ0YvQ0xFLkYxMi5oZXQiKSAlPiUKICBzZWxlY3QoLU5fU0lURVMpCgppc3RhdHMgPC0gbGVmdF9qb2luKGlzdGF0cywgRmlzKQoKcDEgPC0gZ2dwbG90KGlzdGF0cywgYWVzKHggPSBNRUFOLCB5ID0gRl9NSVNTLCBmaWxsID0gYEZgKSkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAyMSwgc2l6ZSA9IDIsIGNvbG9yID0gImJsYWNrIikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXJlY3Rpb24gPSAxLCBvcHRpb24gPSAidmlyaWRpcyIsCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAnRmlzJywgZGlzY3JldGUgPSBGQUxTRSkgKwogIHRoZW1lX3N0YW5kYXJkCgpwMiA8LSBnZ3Bsb3QoaXN0YXRzLCBhZXMoeCA9IENPRUZGX1ZBUiwgeSA9IE1FQU4sIGZpbGwgPSBGX01JU1MpKSArCiBnZW9tX3BvaW50KHNoYXBlID0gMjEsIHNpemUgPSAyLCBjb2xvciA9ICJibGFjayIpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlyZWN0aW9uID0gLTEsIG9wdGlvbiA9ICJ2aXJpZGlzIiwKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICclIG1pc3NpbmcnLCBkaXNjcmV0ZSA9IEZBTFNFKSArCiAgdGhlbWVfc3RhbmRhcmQKCnAzIDwtIGdncGxvdChpc3RhdHMsIGFlcyh4ID0gYE8oSE9NKWAsIHkgPSBNRUFOLCBmaWxsID0gRl9NSVNTKSkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAyMSwgc2l6ZSA9IDIsIGNvbG9yID0gImJsYWNrIikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhkaXJlY3Rpb24gPSAtMSwgb3B0aW9uID0gInZpcmlkaXMiLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gJ21lYW4gcmVhZCBkZXB0aCcsIGRpc2NyZXRlID0gRkFMU0UpICsKICB0aGVtZV9zdGFuZGFyZAoKcDQgPC0gZ2dwbG90KGlzdGF0cywgYWVzKHggPSBNRUFOKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMTAsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIHRoZW1lX3N0YW5kYXJkCgpwNSA8LSBnZ3Bsb3QoaXN0YXRzLCBhZXMoeCA9IGBGYCwgeSA9IE1FQU4sIGZpbGwgPSBGX01JU1MpKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDIxLCBzaXplID0gMiwgY29sb3IgPSAiYmxhY2siKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKGRpcmVjdGlvbiA9IC0xLCBvcHRpb24gPSAidmlyaWRpcyIsCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAnJSBtaXNzaW5nJywgZGlzY3JldGUgPSBGQUxTRSkgKwogIHRoZW1lX3N0YW5kYXJkCgpwNiA8LSBnZ3Bsb3QoaXN0YXRzLCBhZXMoeCA9IGBGYCwgeSA9IENPRUZGX1ZBUiwgZmlsbCA9IEZfTUlTUykpICsKICBnZW9tX3BvaW50KHNoYXBlID0gMjEsIHNpemUgPSAyLCBjb2xvciA9ICJibGFjayIpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlyZWN0aW9uID0gLTEsIG9wdGlvbiA9ICJ2aXJpZGlzIiwKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICclIG1pc3NpbmcnLCBkaXNjcmV0ZSA9IEZBTFNFKSArCiAgdGhlbWVfc3RhbmRhcmQKCnA3IDwtIGdncGxvdChpc3RhdHMsIGFlcyh4ID0gYEZgLCB5ID0gUkFUSU9fTUVBTl9NRURJQU4sIGZpbGwgPSBGX01JU1MpKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDIxLCBzaXplID0gMiwgY29sb3IgPSAiYmxhY2siKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKGRpcmVjdGlvbiA9IC0xLCBvcHRpb24gPSAidmlyaWRpcyIsCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAnJSBtaXNzaW5nJywgZGlzY3JldGUgPSBGQUxTRSkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAuOCwgNCkpICsKICB0aGVtZV9zdGFuZGFyZAoKcDggPC0gZ2dwbG90KGlzdGF0cywgYWVzKHggPSBGX01JU1MpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjAxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICB0aGVtZV9zdGFuZGFyZAoKCm0xNGIgPC0gbXVsdGlwbG90KHAxLCBwMiwgcDMsIHA0LCBwNSwgcDYsIHA3LCBwOCwgY29scz0yKQoKYGBgCgpGbGFnIGxvdyBxdWFsaXR5IGluZGl2aWR1YWxzCgpgYGB7cn0KCiMgVmlldyhpc3RhdHMpCgpMUV9pbmQgPC0gaXN0YXRzICU+JQogICBmaWx0ZXIoYEZgID4gLjkgfCBgRmAgPCAtLjMpICU+JQogICBzZWxlY3QoSU5EVikKCndyaXRlLnRhYmxlKExRX2luZCwgImRhdGEvVkNGL0YxM19MUS5pbmR2IiwKICAgICAgICAgICAgY29sLm5hbWVzID0gVFJVRSwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UpCgpgYGAKCiMjIyBGaWx0ZXIgMTQ6IE1pc3NpbmcgZGF0YSBieSBzYW1wbGUgbG9jYXRpb24KCmBgYHtiYXNoIG1pc3NpbmcgYnkgc2FtcGxlIGxvY2F0fQoKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjEyLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvdGVtcC9DTEUuRjEzIC0tcmVtb3ZlIGRhdGEvVkNGL0YxM19MUS5pbmR2IC0tcmVjb2RlIC0tcmVjb2RlLUlORk8tYWxsCgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMTMucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9GMTMgLS1rZWVwIGRhdGEvVkNGL2R1cGxpY2F0ZS5pbmQgLS0wMTIKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjEzLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvRjEzIC0ta2VlcCBkYXRhL1ZDRi9kdXBsaWNhdGUuaW5kIC0tZ2Vuby1kZXB0aAoKIyBBcmFuc2FzIEJheQp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMTMucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi90ZW1wL0FSQSAtLWtlZXAgZGF0YS9WQ0YvQVJBLmluZCAgLS1yZWNvZGUgLS1yZWNvZGUtSU5GTy1hbGwKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9BUkEucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9BUkEgLS1taXNzaW5nLXNpdGUKCiMgQ29ycHVzIENocmlzdGkgQmF5CnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxMy5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL3RlbXAvQ0MgLS1rZWVwIGRhdGEvVkNGL0NDLmluZCAgLS1yZWNvZGUgLS1yZWNvZGUtSU5GTy1hbGwKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DQy5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NDIC0tbWlzc2luZy1zaXRlCgojIEdhbHZlc3RvbiBCYXkKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjEzLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvdGVtcC9HQUwgLS1rZWVwIGRhdGEvVkNGL0dBTC5pbmQgIC0tcmVjb2RlIC0tcmVjb2RlLUlORk8tYWxsCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvR0FMLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvR0FMIC0tbWlzc2luZy1zaXRlCgojIE1hdGFnb3JkYSBCYXkKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjEzLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvdGVtcC9NQVQgLS1rZWVwIGRhdGEvVkNGL01BVC5pbmQgIC0tcmVjb2RlIC0tcmVjb2RlLUlORk8tYWxsCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvTUFULnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvTUFUIC0tbWlzc2luZy1zaXRlCgojIFNhbiBBbnRvbmlvIEJheQp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMTMucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi90ZW1wL1NBIC0ta2VlcCBkYXRhL1ZDRi9TQS5pbmQgIC0tcmVjb2RlIC0tcmVjb2RlLUlORk8tYWxsCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvU0EucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9TQSAtLW1pc3Npbmctc2l0ZQoKIyBTYWJpbmUgTGFrZSAKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjEzLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvdGVtcC9TTCAtLWtlZXAgZGF0YS9WQ0YvU0wuaW5kICAtLXJlY29kZSAtLXJlY29kZS1JTkZPLWFsbAp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL1NMLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvU0wgLS1taXNzaW5nLXNpdGUKCmBgYAoKQ29tcGFyZSBtaXNzaW5nIGRhdGEgcGVyIHNhbXBsZSByZWdpb246CgpgYGB7ciBjb21wYXJlIG1pc3NpbmcgYnkgbG9jYXQsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIGNyZWF0ZSBlbXB0eSBsaXN0CmxvY19taXNzaW5nIDwtIGxpc3QoKQoKIyBpbXBvcnQgbWlzc2luZyBkYXRhIHBlciBsb2N1cwpsb2NfbWlzc2luZ1tbMV1dIDwtIHJlYWQudGFibGUoImRhdGEvVkNGL0FSQS5sbWlzcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUKICAgc2VsZWN0KENIUiwgUE9TLCBGX01JU1MpICU+JQogICBtdXRhdGUoUE9QID0gIkFSQSIpCiAKIGxvY19taXNzaW5nW1syXV0gPC0gcmVhZC50YWJsZSgiZGF0YS9WQ0YvQ0MubG1pc3MiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lCiAgIHNlbGVjdChDSFIsIFBPUywgRl9NSVNTKSAlPiUKICAgbXV0YXRlKFBPUCA9ICJDQyIpCgpsb2NfbWlzc2luZ1tbM11dIDwtIHJlYWQudGFibGUoImRhdGEvVkNGL0dBTC5sbWlzcyIsIAogICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIHNlbGVjdChDSFIsIFBPUywgRl9NSVNTKSAlPiUKICBtdXRhdGUoUE9QID0gIkdBTCIpCgpsb2NfbWlzc2luZ1tbNF1dIDwtIHJlYWQudGFibGUoImRhdGEvVkNGL01BVC5sbWlzcyIsIAogICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIHNlbGVjdChDSFIsIFBPUywgRl9NSVNTKSAlPiUKICBtdXRhdGUoUE9QID0gIk1BVCIpCgpsb2NfbWlzc2luZ1tbNV1dIDwtIHJlYWQudGFibGUoImRhdGEvVkNGL1NBLmxtaXNzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lCiAgc2VsZWN0KENIUiwgUE9TLCBGX01JU1MpICU+JQogIG11dGF0ZShQT1AgPSAiU0EiKQoKbG9jX21pc3NpbmdbWzZdXSA8LSByZWFkLnRhYmxlKCJkYXRhL1ZDRi9TTC5sbWlzcyIsIAogICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIHNlbGVjdChDSFIsIFBPUywgRl9NSVNTKSAlPiUKICBtdXRhdGUoUE9QID0gIlNMIikKCiMgY3JlYXRlIGRhdGEgZnJhbWUgd2l0aCBhbGwgaW5mb3JtYXRpb24KbG9jX21pc3NpbmcgPC0gbGRwbHkobG9jX21pc3NpbmcsIGRhdGEuZnJhbWUpCgpnZ3Bsb3QobG9jX21pc3NpbmcsIGFlcyh4ID0gRl9NSVNTKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gLjAyNSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IDAuMSksCiAgICAgICAgICAgICBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAibWlzc2luZyBkYXRhIHBlciBsb2N1cyIpICsKICBzY2FsZV95X3NxcnQoKSArCiAgZmFjZXRfZ3JpZChQT1AgfiAuKSArCiAgdGhlbWVfc3RhbmRhcmQKCmBgYAoKaWRlbnRpZnkgbG9jaSB3aXRoIGhpZ2ggbWlzc2luZyBkYXRhIGluIGVhY2ggc2FtcGxlIGxvY2F0aW9uCgpgYGB7cn0KIyBpZGVudGlmeSBsb2NpIHdpdGggaGlnaCBtaXNzaW5nIGRhdGEgaW4gZWFjaCByZWdpb24KU05QcyA8LSBmaWx0ZXIobG9jX21pc3NpbmcsIEZfTUlTUyA+IDAuMSkgCgpjb3VudChTTlBzLCBQT1ApCgpjb250aWdzIDwtIFNOUHMgJT4lCiAgZGlzdGluY3QoQ0hSKQoKU05QcyA8LSBTTlBzICU+JQogIHNlbGVjdChDSFIsIFBPUykKCiMgV3JpdGUgY29udGlnL3Bvc2l0aW9uIHRvIHRleHQgZmlsZSwgdXNlIGZpbGUgd2l0aCB2Y2Z0b29scyB0byByZW1vdmUgcG9zaXRpb25zIGZyb20gZGF0YXNldCAKd3JpdGUudGFibGUoU05QcywgZmlsZSA9ICJkYXRhL1ZDRi9MUV9GMTMubG9jaSIsIAogICAgICAgICAgICBjb2wubmFtZXM9IEZBTFNFLCByb3cubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSkKYGBgCgpSZW1vdmUgbG9jaSBmcm9tIGRhdGEgc2V0OgoKYGBge2Jhc2h9Cgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMTMucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi90ZW1wL0NMRS5GMTQgLS1leGNsdWRlLXBvc2l0aW9ucyBkYXRhL1ZDRi9MUV9GMTMubG9jaSAtLXJlY29kZSAtLXJlY29kZS1JTkZPLWFsbAoKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjE0LnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvRjE0IC0ta2VlcCBkYXRhL1ZDRi9kdXBsaWNhdGUuaW5kIC0tMDEyCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxNC5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0YxNCAtLWtlZXAgZGF0YS9WQ0YvZHVwbGljYXRlLmluZCAtLWdlbm8tZGVwdGgKCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxNC5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMTQgLS1zaXRlLXF1YWxpdHkKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjE0LnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxNCAtLWRlcHRoCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxNC5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMTQgLS1zaXRlLW1lYW4tZGVwdGgKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjE0LnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxNCAtLW1pc3NpbmctaW5kdgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5GMTQucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuRjE0IC0tbWlzc2luZy1zaXRlCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxNC5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5GMTQgLS1oZXQKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjE0LnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxNCAtLWdlbm8tZGVwdGgKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjE0LnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxNCAtLXNpbmdsZXRvbnMKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjE0LnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxNCAtLWluZHYtZnJlcS1idXJkZW4KdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjE0LnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLkYxNCAtLWZyZXEKCmBgYAoKQ29tcGFyZSBzdGF0czoKCmBgYHtyIHN0YXRzIEYxNCwgZmlnLmhlaWdodD0yMCwgZmlnLndpZHRoPTEwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKIyBsb2FkIHN0YXRzIGZpbGVzIC0tLS0KaW5kX3N0YXRzX0YxNCA8LSByZWFkLmluZC5zdGF0cyhkaXIgPSAiZGF0YS9WQ0YiLCB2Y2YgPSAiQ0xFLkYxNCIpICU+JQogIHNlcGFyYXRlKElORFYsIGludG8gPSBjKCJTUCIsICJMSUIiLCAiU0FNUExFX0lEIiksIHNlcCA9ICJfIiwgcmVtb3ZlID0gRkFMU0UsIGV4dHJhID0gIm1lcmdlIikKCmxvY19zdGF0c19GMTQgPC0gcmVhZC5sb2Muc3RhdHMoZGlyID0gImRhdGEvVkNGIiwgdmNmID0gIkNMRS5GMTQiKQoKIyBwbG90IG1pc3NpbmcgZGF0YSBwZXIgaW5kdiAtLS0tCnAxIDwtIGdncGxvdChpbmRfc3RhdHNfRjE0LCBhZXMoeCA9IE1JU1NfQ0xFLkYxNCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IC4wMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oTUlTU19DTEUuRjE0LCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAwLjI1KSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIm1pc3NpbmcgZGF0YSBwZXIgaW5kdiIpICsKICB0aGVtZV9zdGFuZGFyZAoKIyBwbG90IHJlYWQgZGVwdGggcGVyIGluZHYgLS0tLQpwMiA8LSBnZ3Bsb3QoaW5kX3N0YXRzX0YxNCwgYWVzKHggPSBNRUFOX0RFUFRIX0NMRS5GMTQpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMCwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oTUVBTl9ERVBUSF9DTEUuRjE0LCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAyMCksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJtZWFuIHJlYWQgZGVwdGggcGVyIGluZHYiKSArCiAgdGhlbWVfc3RhbmRhcmQKCiMgcGxvdCBkZXB0aCB2cyBtaXNzaW5nIC0tLS0KcDMgPC0gZ2dwbG90KGluZF9zdGF0c19GMTQsIGFlcyh4ID0gTUVBTl9ERVBUSF9DTEUuRjE0LCB5ID0gTUlTU19DTEUuRjE0KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oTUVBTl9ERVBUSF9DTEUuRjE0LCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAyMCksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtZWFuKE1JU1NfQ0xFLkYxNCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMC4yNSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJtZWFuIGRlcHRoIHBlciBpbmR2IiwgeSA9ICIlIG1pc3NpbmcgZGF0YSIpICsKICB0aGVtZV9zdGFuZGFyZAoKIyBwbG90IEZpcyBwZXIgaW5kdiAtLS0tCnA0IDwtIGdncGxvdChpbmRfc3RhdHNfRjE0LCBhZXMoeCA9IEZpc19DTEUuRjE0KSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gLjAxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihGaXNfQ0xFLkYxNCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMCksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJGaXMgcGVyIGluZHYiKSArCiAgdGhlbWVfc3RhbmRhcmQKCiMgcGxvdCBGaXMgdnMgbWlzc2luZyBkYXRhIHBlciBpbmR2IC0tLS0KcDUgPC0gZ2dwbG90KGluZF9zdGF0c19GMTQsIGFlcyh4ID0gRmlzX0NMRS5GMTQsIHkgPSBNSVNTX0NMRS5GMTQpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihGaXNfQ0xFLkYxNCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMCksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtZWFuKE1JU1NfQ0xFLkYxNCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMC4yNSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJGaXMgcGVyIGluZHYiLCB5ID0gIiUgbWlzc2luZyBkYXRhIikgKwogIHRoZW1lX3N0YW5kYXJkCgojIHBsb3QgRmlzIHZzIG1lYW4gZGVwdGggcGVyIGluZHYgLS0tLQpwNiA8LSBnZ3Bsb3QoaW5kX3N0YXRzX0YxNCwgYWVzKHggPSBGaXNfQ0xFLkYxNCwgeSA9IE1FQU5fREVQVEhfQ0xFLkYxNCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKEZpc19DTEUuRjE0LCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAwKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1lYW4oTUVBTl9ERVBUSF9DTEUuRjE0LCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSAyMCksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJGaXMgcGVyIGluZHYiLCB5ID0gIm1lYW4gZGVwdGggcGVyIGluZHYiKSArCiAgdGhlbWVfc3RhbmRhcmQKCgojIHBsb3QgZGlzdHJpYnV0aW9uIG1pc3NpbmcgZGF0YSBwZXIgbG9jdXMgLS0tLQpwNyA8LSBnZ3Bsb3QobG9jX3N0YXRzX0YxNCwgYWVzKHggPSBNSVNTX0NMRS5GMTQpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjAxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihNSVNTX0NMRS5GMTQsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IDAuMSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICIlIG1pc3NpbmcgZGF0YSBwZXIgbG9jdXMiKSArCiAgdGhlbWVfc3RhbmRhcmQKCiMgcGxvdCBkaXN0cmlidXRpb24gbWVhbiByZWFkIGRlcHRoIC0tLS0KcDggPC0gZ2dwbG90KGxvY19zdGF0c19GMTQsIGFlcyh4ID0gTUVBTl9ERVBUSF9DTEUuRjE0KSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oTUVBTl9ERVBUSF9DTEUuRjE0LCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAyMCksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJtZWFuIHJlYWQgZGVwdGggcGVyIGxvY3VzIikgKwogIHRoZW1lX3N0YW5kYXJkCgojIHBsb3QgcmVhZCBkZXB0aCB2cyBtaXNzaW5nIGRhdGEgLS0tLQpwOSA8LSBnZ3Bsb3QobG9jX3N0YXRzX0YxNCwgYWVzKHggPSBNRUFOX0RFUFRIX0NMRS5GMTQsIHkgPSBNSVNTX0NMRS5GMTQpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihNRUFOX0RFUFRIX0NMRS5GMTQsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IDIwKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1lYW4oTUlTU19DTEUuRjE0LCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSAwLjEpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAibWVhbiBkZXB0aCBwZXIgbG9jdXMiLCB5ID0gIiUgbWlzc2luZyBkYXRhIikgKwogIHRoZW1lX3N0YW5kYXJkCgojIHBsb3QgZGVwdGggdnMgU05QIHF1YWxpdHkgLS0tLQpzaXRlX3F1YWwgPC0gcmVhZC50YWJsZSgiZGF0YS9WQ0YvQ0xFLkYxNC5scXVhbCIsIAogICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIG11dGF0ZShQUk9CID0gMTBeKC1RVUFMLzEwKSkKCnRlbXAgPC0gZGF0YS5mcmFtZShsb2Nfc3RhdHNfRjE0JE1FQU5fREVQVEhfQ0xFLkYxNCwgc2l0ZV9xdWFsJFFVQUwpICU+JQogIHJlbmFtZShkZXB0aCA9IGxvY19zdGF0c19GMTQuTUVBTl9ERVBUSF9DTEUuRjE0LCBxdWFsID0gc2l0ZV9xdWFsLlFVQUwpCgpwMTAgPC0gZ2dwbG90KHRlbXAsIGFlcyh4ID0gZGVwdGgsIHkgPSBxdWFsKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihkZXB0aCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMjApLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWVhbihxdWFsLCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSAyMCksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJtZWFuIGRlcHRoIHBlciBsb2N1cyIsIHkgPSAiU05QIHF1YWxpdHkiKSArCiAgdGhlbWVfc3RhbmRhcmQKCiMgcGxvdCBudW1iZXIgb2YgU05QcyBwZXIgY29udGlnIHZzLiBtZWFuIGRlcHRoIC0tLS0KdGVtcCA8LSBsb2Nfc3RhdHNfRjE0ICU+JQogIGNvdW50KENIUikKCnAxMSA8LSBsZWZ0X2pvaW4odGVtcCwgbG9jX3N0YXRzX0YxNCkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBuLCB5ID0gTUVBTl9ERVBUSF9DTEUuRjE0KSkgKwogIGxhYnMoeCA9ICJudW1iZXIgb2YgU05QcyBwZXIgY29udGlnIiwgeSA9ICJtZWFuIGRlcHRoIikgKwogIHRoZW1lX3N0YW5kYXJkCgojIHBsb3Qgbm8gb2YgU05QcyBwZXIgbG9jdXMgLS0tLQpwMTIgPC0gbG9jX3N0YXRzX0YxNCAlPiUKICBjb3VudChDSFIpICU+JQogIGdncGxvdChhZXMoeCA9IG4pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsgCiAgbGFicyh4ID0gIm51bWJlciBvZiBTTlBzIHBlciBsb2N1cyIpICsKICB0aGVtZV9zdGFuZGFyZAoKbTE1IDwtIG11bHRpcGxvdChwMSwgcDIsIHAzLCBwNCwgcDUsIHA2LCBwNywgcDgsIHA5LCBwMTAsIHAxMSwgcDEyLCBjb2xzPTIpCgpgYGAKCkNvbXBhcmUgbnVtYmVyIG9mIGNvbnRpZ3MgdnMgbnVtYmVyIG9mIFNOUHMuCgpgYGB7cn0KCiMgbnVtYmVyIG9mIFNOUHMKbnJvdyhsb2Nfc3RhdHNfRjE0KQoKIyBudW1iZXIgb2YgY29udGlncyBpbiBkYXRhIHNldApjb250aWdzIDwtIGxvY19zdGF0c19GMTQgJT4lCiAgZGlzdGluY3QoQ0hSKQoKbnJvdyhjb250aWdzKQoKYGBgCgpNZWFuIG51bWJlciBvZiBTTlBzIHBlciBjb250aWcgaXMgYHIgbnJvdyhsb2Nfc3RhdHNfRjE0KS9ucm93KGNvbnRpZ3MpYC4KCmBgYHtyfQoKaXN0YXRzIDwtIGxlZnRfam9pbihpbmRfc3RhdHNfcmF3LCBpbmRfc3RhdHNfRjE0KQoKYGBgCgoKIyMgRmluYWwgdGhyZXNob2xkcyB2YWx1ZXMgZm9yIGZpbHRlcmVkIGRhdGEgc2V0IENMRToKCiogbWluaW11bSBzZXF1ZW5jZSBxdWFsaXR5IChtaW5RKTogMjAKKiBtaW5pbXVtIGdlbm90eXBlIHF1YWxpdHkgKG1pbkdRKTogMjAKKiBtaW5pbXVtIGdlbm90eXBlIGNhbGwgcmF0ZSBwZXIgbG9jdXMgKGdlbm8pOiA5MCUKKiBtaW5pbXVtIGdlbm90eXBlIGRlcHRoIChtaW5EKTogNQoqIG1heGltdW0gbWlzc2luZyBkYXRhIHBlciBpbmRpdmlkdWFsIChtaXNzSW5kKTogMjUlCiogbWVhbiBtaW5pbXVtIGRlcHRoIHBlciBsb2N1cyAobS1taW5EKTogMTUKKiBtZWFuIG1heGltdW0gZGVwdGggKG0tbWluRCk6IDE4MAoqIGREb2NlbnQgZmlsdGVycyBydW4gZm9yIGFsbGVsaWMgYmFsYW5jZSwgbWFwcGluZyBxdWFsaXR5LCBzdHJhbmRlZG5lc3MgYW5kIHBhaXJlZCBzdGF0dXMsIGRlcHRoL3F1YWxpdHkgcmF0aW8KKiBJbmRlbHMgcmVtb3ZlZCBmcm9tIGRhdGEgc2V0CiogbWluaW11bSBnZW5vdHlwZSBjYWxsIHJhdGUgYnkgcG9wdWxhdGlvbjogOTAlCgpEYXRhIHNldCBjb250YWlucyBgciBucm93KGxvY19zdGF0c19GMTQpYCBTTlAgc2l0ZXMgb24gYHIgbnJvdyhjb250aWdzKWAgYW5kIGByIG5yb3coaW5kX3N0YXRzX0YxNClgIGluZGl2aWR1YWxzLgoKIyBIYXBsb3R5cGluZwoKIyMgUHJlcCBmb3IgaGFwbG90eXBpbmcKCkNyZWF0ZSBsaXN0IG9mIGluZGl2aWR1YWxzIHJldGFpbmVkIGluIGZpbmFsIHZjZiBmaWxlOgoKQ2hhbmdlIHNvIHRoYXQgbmV3IGZpbGUgaXMgcHJpbnRlZCBpbnRvIEhhcGxvdHlwaW5nL3RlbXAgZm9sZGVyCgpgYGB7YmFzaH0KCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLkYxNC5yZWNvZGUudmNmIC0tb3V0IGRhdGEvSGFwbG90eXBpbmcvdGVtcC9DTEUgLS1yZWNvZGUgLS1yZWNvZGUtSU5GTy1hbGwKCnZjZnNhbXBsZW5hbWVzIGRhdGEvSGFwbG90eXBpbmcvdGVtcC9DTEUucmVjb2RlLnZjZiA+IGRhdGEvSGFwbG90eXBpbmcvdGVtcC9DTEUuaW5kaXZpZHVhbHMKCmBgYAoKVXNlIGBDTEUtQ0xFLmluZGl2aWR1YWxzYCB0byBjcmVhdGUgYHBvcG1hcGAgYXMgYSB0YWItc2VwYXJhdGVkIGZpbGUgb2YgaW5kaXZpZHVhbHMgYW5kIHRoZWlyIHBvcHVsYXRpb24gZGVzaWduYXRpb24sIHdpdGggb25lIGluZGl2aWR1YWwgcGVyIGxpbmUgKG1ha2Ugc3VyZSBVTklYIGZvcm1hdCkuIFRoaXMgZmlsZSBpcyBuZWVkZWQgdG8gd3JpdGUgdGhlIGdlbmVwb3AgZmlsZSwgaWYgbm90IHByb3ZpZGVkIHRoZSBzY3JpcHQgd2lsbCBydW4gdGhyb3VnaCB0aGUgcHJvY2VzcyBidXQgbm90IHdyaXRlIGEgZ2VuZXBvcCBmaWxlIGFuZCBwbGFjZSBpbnRvIHNhbWUgZm9sZGVyIGByYWRfaGFwbG90eXBlci5wbGAgd2lsbCBiZSBydW4gZnJvbS4KCmBgYHtyfQoKcG9wbWFwIDwtIHJlYWQudGFibGUoImRhdGEvSGFwbG90eXBpbmcvdGVtcC9DTEUuaW5kaXZpZHVhbHMiLCAKICAgICAgICAgICAgICAgICAgICAgY29sLm5hbWVzID0gIklORFYiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIHNlcGFyYXRlKElORFYsIGludG8gPSBjKCJQT1AiLCAiTElCIiwgIklEIiksIHNlcCA9ICJfIiwgcmVtb3ZlID0gRkFMU0UsIGV4dHJhID0gIm1lcmdlIikgJT4lCiAgc2VsZWN0KElORFYsIFBPUCkKCndyaXRlLnRhYmxlKHBvcG1hcCwgImRhdGEvSGFwbG90eXBpbmcvdGVtcC9wb3BtYXAiLCAKICAgICAgICAgICAgY29sLm5hbWVzID0gRkFMU0UsIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFKQoKYGBgCgpQbGFjZSBhbGwgbmVjZXNzYXJ5IGZpbGVzIGluIGBkYXRhL0hhcGxvdHlwaW5nL3RlbXBgIGRpcmVjdG9yeSAoYmFtLCBmYXN0cSwgcmVmZXJlbmNlLmZhc3RhLCB2Y2YtZmlsZSkKCmBgYHtiYXNoLCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQoKbG4gLXMgL2hvbWUvc29sZWFyeS9CVUxMU0hBUktTL0NMRV9QT1BHRU4vZGF0YS9TRVEvQ0xFLTIvKi5mcS5neiAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL0hhcGxvdHlwaW5nL3RlbXAKbG4gLXMgL2hvbWUvc29sZWFyeS9CVUxMU0hBUktTL0NMRV9QT1BHRU4vZGF0YS9TRVEvQ0xFLTIvKi5iYW0qIC9ob21lL3NvbGVhcnkvQlVMTFNIQVJLUy9DTEVfUE9QR0VOL2RhdGEvSGFwbG90eXBpbmcvdGVtcApsbiAtcyAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL1NFUS9DTEUtMy8qLmZxLmd6IC9ob21lL3NvbGVhcnkvQlVMTFNIQVJLUy9DTEVfUE9QR0VOL2RhdGEvSGFwbG90eXBpbmcvdGVtcApsbiAtcyAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL1NFUS9DTEUtMy8qLmJhbSogL2hvbWUvc29sZWFyeS9CVUxMU0hBUktTL0NMRV9QT1BHRU4vZGF0YS9IYXBsb3R5cGluZy90ZW1wCgpgYGAKCiMjIFJ1biBIYXBsb3R5cGVyCgpgYGB7YmFzaCwgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KCiMgZGlmZmljdWx0eSBydW5uaW5nIGZyb20gUgpub2h1cCByYWRfaGFwbG90eXBlci5wbCAtdiBDTEUucmVjb2RlLnZjZiAtciByZWZlcmVuY2UuZmFzdGEgLXggMzUgLWQgMTAgLXogMyAtbSAuMjUgLWcgQ0xFLmdlbiAtcCBwb3BtYXAKCmBgYAoKQ29weSBmaWxlcyBmb3IgYW5hbHlzaXMgaW50byBgZGF0YS9IYXBsb3R5cGluZy9gLWZvbGRlcgoKYGBge2Jhc2h9CgpjZCAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL0hhcGxvdHlwaW5nL3RlbXAKcm0gKi5mcS5negpybSAqLmJhbSoKCmNwIC9ob21lL3NvbGVhcnkvQlVMTFNIQVJLUy9DTEVfUE9QR0VOL2RhdGEvSGFwbG90eXBpbmcvdGVtcC9pbmRfc3RhdHMub3V0IC9ob21lL3NvbGVhcnkvQlVMTFNIQVJLUy9DTEVfUE9QR0VOL2RhdGEvSGFwbG90eXBpbmcvCgpjcCAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL0hhcGxvdHlwaW5nL3RlbXAvc3RhdHMub3V0IC9ob21lL3NvbGVhcnkvQlVMTFNIQVJLUy9DTEVfUE9QR0VOL2RhdGEvSGFwbG90eXBpbmcvCgpjcCAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL0hhcGxvdHlwaW5nL3RlbXAvQ0xFLmdlbiAvaG9tZS9zb2xlYXJ5L0JVTExTSEFSS1MvQ0xFX1BPUEdFTi9kYXRhL0hhcGxvdHlwaW5nLwoKYGBgCgojIEhhcGxvdHlwZSBmaWx0ZXJpbmcKCiMjIE92ZXJ2aWV3IG9mIGhhcGxvdHlwaW5nIHN1Y2Nlc3MKCkNvbXBhcmlzb24gb2YgdGhlIG51bWJlciBvZiBsb2NpIHRoYXQgd2VyZSBmaWx0ZXJlZCBkdWUgdG8gZXhjZXNzIG51bWJlciBvZiBtaXNzaW5nIGRhdGEgb3Igc3VzcGVjdGVkIHBhcmFsb2dzIGR1cmluZyB0aGUgaGFwbG90eXBpbmcgcHJvY2VzcyB0byB0aG9zZSB0aGF0IHBhc3NlZC4gSGFwbG90eWVyIHdhcyBydW4gd2l0aG91dCBhbnkgdGhyZXNob2xkIHZhbHVlcyAob3RoZXIgdGhhbiBkZWZhdWx0IHZhbHVlcykuIFRoZSBnZW5lcG9wIGZpbGUgb25seSBjb250YWlucyBsb2NpIHRoYXQgcGFzc2VkIGhhcGxvdHlwaW5nIHN0YWdlLgoKYGBge3IgaW1wb3J0IHN0YXRzLm91dCBmaWxlLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0zLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKIyBpbXBvcnQgc3RhdHMgZmlsZXMgZ2VuZXJhdGVkIGJ5IGhhcGxvdHlwZXIKaGFwX3N0YXRzIDwtIHJlYWQuaGFwLnN0YXRzKCJkYXRhL0hhcGxvdHlwaW5nL3N0YXRzLm91dCIpCgoKCmhhcF9pbmRfc3RhdHMgPC0gcmVhZC5kZWxpbSgiZGF0YS9IYXBsb3R5cGluZy9pbmRfc3RhdHMub3V0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKIyB2aWV3IGNvbXBhcmlzb24gb2YgZmlsdGVyZWQgdnMuIHBhc3NlZCBsb2NpCnBsb3QuZmlsdGVyLnN0YXR1cyhoYXBfc3RhdHMpCgpgYGAKClJlbW92ZSBmaWx0ZXJlZCBsb2NpIGZyb20gZGF0YSBzZXQuCgpgYGB7cn0KCmNvdW50KGhhcF9zdGF0cywgU3RhdHVzID09ICJGSUxURVJFRCIpCgpoYXBfc3RhdHMgPC0gcmVtb3ZlLmZpbHRlcmVkLmhhcHMoaGFwX3N0YXRzKQoKYGBgCgpgciBucm93KGhhcF9zdGF0cylgIGxvY2kgcGFzc2VkIGhhcGxvdHlwaW5nIHByb2Nlc3MuCgpgYGB7ciBwbG90IHN0YXRzLm91dCBmaWxlLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD05LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKcGxvdC5oYXAuc3RhdHMoaGFwX3N0YXRzKQoKYGBgCgpEYXRhIHNldCBjb250YWlucyBgciBucm93KGhhcF9pbmRfc3RhdHMpYCBpbmRpdmlkdWFsczoKCmBgYHtyIHBsb3QgaW5kLnN0YXRzLm91dCBmaWxlLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD05LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKcGxvdC5pbmQuaGFwLnN0YXRzKGhhcF9pbmRfc3RhdHMpCgpgYGAKCiMjIElkZW50aWZ5IHRocmVzaG9sZCB2YWx1ZXMgdG8gZmlsdGVyIGRhdGEgc2V0CgojIyMgUHJvcG9ydGlvbiBvZiBpbmRpdmlkdWFscyBoYXBsb3R5cGVkCgpgYGB7ciBwcm9wIGluZHYgaGFwbG90eXBlZCwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIGhhcF9zdGF0cyA8LSByZWFkLmhhcC5zdGF0cygiZGF0YS9IYXBsb3R5cGluZy9zdGF0cy5vdXQiKQoKcDEgPC0gZ2dwbG90KGhhcF9zdGF0cywgYWVzKHggPSBQcm9wX0hhcGxvdHlwZWQpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjAyNSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oUHJvcF9IYXBsb3R5cGVkLCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMC45KSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gInByb3BvcnRpb24gaW5kaXZpZHVhbHMgaGFwbG90eXBlZCIpICsKICB0aGVtZV9zdGFuZGFyZAoKcDIgPC0gZ2dwbG90KGhhcF9zdGF0cywgYWVzKHggPSBQcm9wX0hhcGxvdHlwZWQsIHkgPSBQb3NzX1BhcmFsb2cpKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDIxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihQcm9wX0hhcGxvdHlwZWQsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJwcm9wb3J0aW9uIGluZGl2aWR1YWxzIGhhcGxvdHlwZWQiLCB5ID0gInBvc3NpYmxlIHBhcmFsb2dzIikgKwogIHRoZW1lX3N0YW5kYXJkCgpwMyA8LSBnZ3Bsb3QoaGFwX3N0YXRzLCBhZXMoeCA9IFByb3BfSGFwbG90eXBlZCwgeSA9IGBMb3dfQ292Lkdlbm9fRXJyYCkpICsKICBnZW9tX3BvaW50KHNoYXBlID0gMjEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKFByb3BfSGFwbG90eXBlZCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya3JlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gInByb3BvcnRpb24gaW5kaXZpZHVhbHMgaGFwbG90eXBlZCIsIHkgPSAibG93IGNvdmVyYWdlIGVycm9yIikgKwogIHRoZW1lX3N0YW5kYXJkCgptdWx0aXBsb3QocDEsIHAyLCBwMywgY29scyA9IDMpCgpgYGAKCkZsYWcgbG9jaSBzdWNjZXNzZnVsbHkgaGFwbG90eXBlZCBpbiA8IDkwJSBvZiBpbmRpdmlkdWFscy4KCmBgYHtyIGZpbHRlciBsb2NpIGdlbm90eXBlIGNhbGwgcmF0ZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiMgbnVtYmVyIG9mIGxvY2kgY2FsbGVkIGluID45NSUgaW5kaXZpZHVhbHMKY291bnQoaGFwX3N0YXRzLCBQcm9wX0hhcGxvdHlwZWQgPCAwLjkpCgojIGNyZWF0ZSB2ZWN0b3Igb2YgbG9jaSB0byByZW1vdmUgKGNob29zZSBjdXQtb2ZmKQpQcm9wX0hhcGxvdHlwZWQgPC0gZmlsdGVyLmxvY2kucHJvcF9oYXBsb3R5cGVkKGhhcF9zdGF0cywgMC45KQoKYGBgCgpgciBsZW5ndGgoUHJvcF9IYXBsb3R5cGVkKWAgbG9jaSB3ZXJlIGZsYWdnZWQgYXMgZ2Vub3R5cGUgY2FsbCByYXRlIDwgMC45LgoKIyMjIFBvc3NpYmxlIHBhcmFsb2dzIHBlciBsb2N1cwoKTG9jaSBhcmUgZmxhZ2dlZCBhcyBwb3NzaWJsZSBwYXJhbG9ncyBmb3IgYW4gaW5kaXZpZHVhbHMgd2hlbiBtb3JlIHRoYW4gdGhlIGV4cGVjdGVkIG51bWJlciBvZiBoYXBsb3R5cGVzIGJhc2VkIG9uIHRoZSBTTlAgZ2Vub3R5cGUgY2FsbCAoaG9tb3p5Z290ZSwgaGV0ZXJvenlnb3RlKSBhcmUgZGV0ZWN0ZWQuCgpgYGB7ciBwb3NzIHBhcmFsb2dzIHBlciBsb2N1cywgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCmdncGxvdChoYXBfc3RhdHMsIGFlcyh4ID0gUG9zc19QYXJhbG9nKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oUG9zc19QYXJhbG9nLCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gKG5yb3coaGFwX2luZF9zdGF0cykqMC4wMSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAicG9zc2libGUgcGFyYWxvZ3MgcGVyIGxvY3VzIikgKwogIHRoZW1lX3N0YW5kYXJkCgpgYGAKCjEgJSBvZiBpbmRpdmlkdWFsczogYHIgbnJvdyhoYXBfaW5kX3N0YXRzKSowLjAxYCAKNSAlIG9mIGluZGl2aWR1YWxzOiBgciBucm93KGhhcF9pbmRfc3RhdHMpKjAuMDVgIAoKRmxhZyBsb2NpIHRoYXQgYXJlIGZsYWdnZWQgYXMgcG90ZW50aWFsIHBhcmFsb2dzIGluIDMgb3IgbW9yZSBpbmRpdmlkdWFscy4KCmBgYHtyIGZpbHRlciBsb2NpIHBhcmFsb2dzfQoKIyBudW1iZXIgb2YgbG9jaSB3aXRoIFBvc3NfUGFyYWxvZ3MKY291bnQoaGFwX3N0YXRzLCBQb3NzX1BhcmFsb2cgPiAyKQoKIyBjcmVhdGUgdmVjdG9yIG9mIGxvY2kgdG8gcmVtb3ZlIChjaG9vc2UgY3V0LW9mZikKUG9zc19QYXJhbG9ncyA8LSBmaWx0ZXIubG9jaS5wYXJhbG9ncyhoYXBfc3RhdHMsIDIpCgpgYGAKCmByIGxlbmd0aChQb3NzX1BhcmFsb2dzKWAgbG9jaSB3ZXJlIGZsYWdnZWQgYXMgcG9zc2libGUgcGFyYWxvZ3MuCgojIyMgTnVtYmVyIG9mIFNOUHMgJiBIYXBsb3R5cGVzIHBlciBsb2N1cwoKRWFjaCBsb2N1cyB2YXJpZXMgaW4gdGhlIG51bWJlciBvZiBTTlBzIGRldGVjdGVkIHdoaWNoIGRldGVybWluZXMgdGhlIG51bWJlciBvZiBoYXBsb3R5cGVzIGV4cGVjdGVkIGluIHRoYXQgcG9wdWxhdGlvbi4gCgpgYGB7ciBubyBTTlBzLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD00LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKZ2dwbG90KGhhcF9zdGF0cywgYWVzKHggPSBTaXRlcykpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKFNpdGVzLCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMjUpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAibnVtYmVyIG9mIFNOUHMgcGVyIGNvbnRpZyIpICsKICB0aGVtZV9zdGFuZGFyZAoKYGBgCgpGaWx0ZXJpbmcgbG9jaSBiYXNlZCBvbiBudW1iZXIgb2YgU05QcyBjb250YWluZWQgb24gdGhhdCBsb2N1cyBjb3VsZCBiaWFzIHRoZSBkYXRhIHNldCBhcyBsb2NpIHdpdGggaGlnaCByZWNvbWJpbmF0aW9uIG1heSBiZSByZW1vdmVkLiBPbiB0aGUgb3RoZXIgaGFuZCwgYXNzdW1pbmcgYW4gYXBwcm94aW1hdGUgbGVuZ3RoIG9mIDI1MCBicCBsb2NpIHdpdGggbW9yZSB0aGFuIGByIDI1MCowLjFgIFNOUHMgd291bGQgbWVhbiB0aGF0IDEwJSBvZiBiYXNlcyBhcmUgYSBwb2x5bW9ycGhpc21zLgoKYGBge3Igc2l0ZXMgdnMgcGFyYWxvZ3MsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTh9CgpwMSA8LSBnZ3Bsb3QoaGFwX3N0YXRzLCBhZXMoeCA9IEhhcGxvdHlwZXMsIHkgPSBQb3NzX1BhcmFsb2cpKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDIxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArCiAgbGFicyh4ID0gIm5vLiBoYXBsb3R5cGVzIiwgeSA9ICJwb3NzaWJsZSBwYXJhbG9ncyIpICsKICB0aGVtZV9zdGFuZGFyZAoKcDIgPC0gZ2dwbG90KGhhcF9zdGF0cywgYWVzKHggPSBTaXRlcywgeSA9IFBvc3NfUGFyYWxvZykpICsKICBnZW9tX3BvaW50KHNoYXBlID0gMjEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKICBsYWJzKHggPSAibm8uIG9mIHNpdGVzIiwgeSA9ICIgIikgKwogIHRoZW1lX3N0YW5kYXJkCiAgCm11bHRpcGxvdChwMSwgcDIsIGNvbHMgPSAyKQoKYGBgCgpJZGVudGlmeSBudW1iZXIgb2YgaGFwbG90eXBlcyBsb2NpIHdpdGggPiAzNSBTTlAgc2l0ZXMuCgpgYGB7ciBmaWx0ZXIgbm8gb2YgU05Qc30KCmNvdW50KGhhcF9zdGF0cywgU2l0ZXMgPiAzNSkKCk5vU05wcyA8LSBmaWx0ZXIubG9jaS5ub1NOUHMoaGFwX3N0YXRzLCAzNSkKCmBgYAoKYHIgbGVuZ3RoKE5vU05wcylgIGxvY2kgd2VyZSBmbGFnZ2VkLgoKQXNzdW1pbmcgdGhhdCBtdXRhdGlvbiBpcyB0aGUgb25seSBtZWNoYW5pc20gcmVzdWx0aW5nIGluIG5ldyBoYXBsb3R5cGVzLCB0aGUgbWF4aW11bSBleHBlY3RlZCBudW1iZXIgb2YgaGFwbG90eXBlcyBwZXIgbG9jdXMgaXMgbnVtYmVyIG9mIFNOUHMgTiArIDEuCgpgYGB7ciB4dHJhIGhhcGxvdHlwZXMsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xMCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCnAxIDwtIGdncGxvdChoYXBfc3RhdHMsIGFlcyh4ID0gSGFwbG90eXBlcykpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKEhhcGxvdHlwZXMsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBxdWFudGlsZShIYXBsb3R5cGVzLCAuOTksIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICIjIGhhcGxvdHlwZXMgcGVyIGxvY3VzIiwgeSA9ICIjIGxvY2kiKSArCiAgdGhlbWVfc3RhbmRhcmQKCnRlbXAgPC0gaGFwX3N0YXRzICU+JQogIHNlbGVjdChMb2N1cywgU2l0ZXMsIEhhcGxvdHlwZXMsIFByb3BfSGFwbG90eXBlZCwgTG93X0Nvdi5HZW5vX0VyciwgUG9zc19QYXJhbG9nKSAlPiUKICBtdXRhdGUoZXhwX3NpdGVzID0gU2l0ZXMgKyAxKSAlPiUKICBtdXRhdGUoeHRyYSA9IEhhcGxvdHlwZXMgLSBTaXRlcykKCnAyIDwtIGdncGxvdCh0ZW1wLCBhZXMoeCA9IFNpdGVzLCB5ID0gUG9zc19QYXJhbG9nKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNSwgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDI1LCBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIiMgU05QIHNpdGVzIHBlciBsb2N1cyIsIHkgPSAicG9zc2libGUgcGFyYWxvZ3MiKSArCiAgdGhlbWVfc3RhbmRhcmQKCnAzIDwtIGdncGxvdCh0ZW1wLCBhZXMoeCA9IFNpdGVzLCB5ID0geHRyYSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEwLCBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMjUsIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAiIyBTTlAgc2l0ZXMgcGVyIGxvY3VzIiwgeSA9ICIjIGV4dHJhIGhhcGxvdHlwZXMiKSArCiAgdGhlbWVfc3RhbmRhcmQKCnA0IDwtIGdncGxvdCh0ZW1wLCBhZXMoeCA9IHh0cmEsIHkgPSBQcm9wX0hhcGxvdHlwZWQpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAuOSwgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDI1LCBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIiMgZXh0cmEgaGFwbG90eXBlcyIsIHkgPSAicHJvcG9ydGlvbiBvZiBpbmR2IGhhcGxvdHlwZWQiKSArCiAgdGhlbWVfc3RhbmRhcmQKCnA1IDwtIGdncGxvdCh0ZW1wLCBhZXMoeCA9IHh0cmEsIHkgPSBMb3dfQ292Lkdlbm9fRXJyKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSA1LCBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMjUsIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAiIyBleHRyYSBoYXBsb3R5cGVzIiwgeSA9ICJwb3RlbnRpYWwgZ2Vub3R5cGluZyBlcnJvciIpICsKICB0aGVtZV9zdGFuZGFyZAoKcDYgPC0gZ2dwbG90KHRlbXAsIGFlcyh4ID0geHRyYSwgeSA9IFBvc3NfUGFyYWxvZykpICsKICBnZW9tX3BvaW50KHNpemUgPSAxKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNSwgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDI1LCBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIiMgZXh0cmEgaGFwbG90eXBlcyIsIHkgPSAicG9zc2libGUgcGFyYWxvZ3MiKSArCiAgdGhlbWVfc3RhbmRhcmQKCm0yMSA8LSBtdWx0aXBsb3QocDEsIHAyLCBwMywgcDQsIHA1LCBwNiwgY29scyA9IDIpCgpgYGAKCkFnYWluLCByZW1vdmluZyBsb2NpIHdpdGggdW5leHBlY3RlZGx5IGhpZ2ggbnVtYmVycyBvZiBoYXBsb3R5cGVzIG1heSBiaWFzIHRoZSBkYXRhIHNldCwgYXMgbG9jaSB3aXRoIGUuZy4gaGlnaCByZWNvbWJpbmF0aW9uIG1heSBiZSByZW1vdmVkIGZyb20gdGhlIGRhdGEgc2V0LgoKYGBge3IgZmlsdGVyIG5vIG9mIGhhcGxvdHlwZXN9Cgpjb3VudCh0ZW1wLCB4dHJhID49IDUwKQoKIyBjcmVhdGUgdmVjdG9yIG9mIGxvY2kgdG8gcmVtb3ZlCk5vSGFwcyA8LSBmaWx0ZXIodGVtcCwgeHRyYSA+PSA1MCkgCgpOb0hhcHMgPC0gTm9IYXBzJExvY3VzCgpgYGAKCmByIGxlbmd0aChOb0hhcHMpYCBsb2NpIHdlcmUgZmxhZ2dlZCBmb3IgZXhjZXNzaXZlIG51bWJlciBvZiBoYXBsb3R5cGVzIGNvbXBhcmVkIHRvIHRoZSBudW1iZXIgb2YgU05QcyBhdCB0aGF0IGxvY3VzLgoKIyMjIExvdyBDb3ZlcmFnZS9HZW5vdHlwaW5nIEVycm9yCgpBZnRlciBjb21iaW5pbmcgU05QcyBvbiB0aGUgc2FtZSBjb250aWcgZHVyaW5nIHRoZSBoYXBsb3R5cGluZyBwcm9jZXNzLCBpdCBpcyBwb3NzaWJsZSB0byBvYnNlcnZlIGZld2VyIHRoYW4gdGhlIGV4cGVjdGVkIG51bWJlciBvZiBoYXBsb3R5cGVzIGJhc2VkIG9uIHRoZSBnZW5vdHlwZSBjYWxsIChoZXRlcm96eWdvdGUvaG9tb3p5Z290ZSkuIFdoZW4gdGhpcyBvY2N1cnMsIHRoYXQgZ2Vub3R5cGUgaXMgY29kZWQgYXMgbWlzc2luZy4gRm9yIGVhY2ggbG9jdXMgdGhlIG51bWJlciBvZiBpbmRpdmlkdWFscyBmb3Igd2hpY2ggaXMgaXQgZmxhZ2dlZCBhcyBhIHBvdGVudGlhbCBhIHBvdGVudGlhbCBnZW5vdHlwaW5nIGVycm9yIG9yIHN1ZmZlcmluZyBmcm9tIG51bGwgYWxsZWxlcyBkdWUgdG8gbG93IGNvdmVyYWdlIGRldGVjdGVkIGZvciBhIGdpdmVuIGxvY3VzIGlzIHJlY29yZGVkLiAKCmBgYHtyIGxvdyBjb3YgZ2Vub3R5cGcgZXJyb3IsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpnZ3Bsb3QoaGFwX3N0YXRzLCBhZXMoeCA9IExvd19Db3YuR2Vub19FcnIpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1LCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihMb3dfQ292Lkdlbm9fRXJyLCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gKG5yb3coaGFwX2luZF9zdGF0cykqMC4wNSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAicG90ZW50aWFsIGdlbm90eXBpbmcgZXJyb3IiKSArCiAgdGhlbWVfc3RhbmRhcmQKCmBgYAoKTG9jaSB3aXRoIHByb25vdW5jZWQgcGF0dGVybnMgb2YgZ2Vub3R5cGluZyBlcnJvciBsaWtlbHkgZHVlIHRvIGxvdyBjb3ZlcmFnZSB3aWxsIGhhdmUgbG93IHN1Y2Nlc3MgcmF0ZSBmb3IgZ2Vub3R5cGluZywgaS5lLiB0aGV5IHdpbGwgYmUgY2F1Z2h0IGluIG1pc3NpbmcgZGF0YSBmaWx0ZXJzLiBDb3ZlcmFnZSBpc3N1ZXMgYXJlIGxpa2VseSBnZW5vdHlwZSBzcGVjZmljOyBwcmV2aW91cyBmaWx0ZXJzIGhhdmUgdGFyZ2V0ZWQgbG9jaSBhbmQgaW5kaXZpZHVhbHMgd2l0aCBoaWdoIHZhcmlhbmNlIGluIGNvdmVyYWdlIGFuZCBzdXNwZWN0IGdlbm90cGVzIGhhdmUgYmVlbiBjb2RlZCBhcyBtaXNzaW5nLCBpLmUuIHRoaXMgZmlsdGVyIG5lZWQgbm90IGJlIHZlcnkgY29uc2VydmF0aXZlLCByZWdhcmRsZXNzLCBsb2NpIHRoYXQgYXJlIHJlcGVhdGVkbHkgYmVpbmcgZmxhZ2dlZCBhcyBwcm9ibGVtYXRpYyBzaG91bGQgYmUgcmVtb3ZlZC4KCmBgYHtyIGZpbHRlciBsb3cgY292ZXJhZ2UvZ2Vub3R5cGluZyBlcnJvciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiMgc3VtbWFyeSBvZiBjb3VudCBvZiBwb3NzaWJsZSBnZW5vdHlwaW5nIGVycm9ycwpjb3VudChoYXBfc3RhdHMsIExvd19Db3YuR2Vub19FcnIgPiAxMCkKCiMgY3JlYXRlIHZlY3RvciBvZiBsb2NpIHRvIHJlbW92ZQpHZW5vX0VyciA8LSBmaWx0ZXIubG9jaS5nZW5vX2VycihoYXBfc3RhdHMsIDEwKQoKYGBgCgpgciBsZW5ndGgoR2Vub19FcnIpYCBsb2NpIHdlcmUgZmxhZ2dlZCBhcyBwb3RlbnRpYWxseSBhZmZlY3RlZCBieSBnZW5vdHlwaW5nIGVycm9yLgoKIyMjIEZsYWcgcHJvYmxlbWF0aWMgaW5kaXZpZHVhbHMKCkxvY2kgdGhhdCBhcmUgbm90IHN1Y2Nlc3NmdWxseSBoYXBsb3R5cGVkIGluIGFuIGluZGl2aWR1YWwgZHVlIHRvIG1pc3NpbmcgZGF0YSwgY29tcGxleCBsb2N1cywgaGFwbG90eXBlZCBnZW5vdHlwZSBpcyBoaWdoZXIvbG93ZXIgdGhhbiBTTlAgaGFwbG90eXBlIGluIGEgZ2l2ZW4gaW5kaXZpZHVhbCBhcmUgY29kZWQgYXMgbWlzc2luZyBmb3IgdGhhdCBpbmRpdmlkdWFsLiBQcm9ibGVtYXRpYyBpbmRpdmlkdWFsIGNhbiBiZSBpZGVudGlmaWVkIGFzIGhhdmluZyBhIGhpZ2ggcHJvcG9ydGlvbiBvZiBtaXNzaW5nIGRhdGEuCgoqKkluZGl2aWR1YWxzIHdpdGggbG93IHByb3BvcnRpb24gb2Ygc3VjY2Vzc2Z1bGx5IGhhcGxvdHlwZWQgbG9jaSoqCgpgYGB7ciBtaXNzaW5nIGluZHYsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpnZ3Bsb3QoaGFwX2luZF9zdGF0cywgYWVzKHggPSBQcm9wX1N1Y2Nlc3MpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjAxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihQcm9wX1N1Y2Nlc3MsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAwLjkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAicHJvcG9ydGlvbiBsb2NpIHN1Y2Nlc3NmdWxseSBoYXBsb3R5cGVkIikgKwogIHRoZW1lX3N0YW5kYXJkCgpgYGAKClByb2JsZW0gaW5kaXZpZHVhbHMgY2FuIGJlIGlkZW50aWZpZWQgYnkgY2hvb3NpbmcgYSBjdXQtb2ZmIHZhbHVlIGZvciB0aGUgbWluaXVtIHByb3BvcnRpb24gb2Ygc3VjZXNzZnVsbHkgaGFwbG90eXBlZCBsb2NpIGFuZCBleGNsdWRpbmcgaW5kaXZpZHVhbHMgYmVsb3cgdGhhdCB0aHJlc2hvbGQgdmFsdWUuIFJlbW92aW5nIGxvY2kgd2l0aCBsb3cgaGFwbG90eXBpbmcgc3VjY2VzcyB3aXRoIGRlY3JlYXNlIHRoZSBhbW91bnQgb2YgbWlzc2luZyBkYXRhLiBUaGVyZWZvcmUsIGZpbmFsIG1pc3NpbmcgZGF0YSBjdXQtb2ZmcyBzaG91bGQgYmUgYXBwbGllZCAqYWZ0ZXIqIHJlbW92aW5nIHRob3NlIGxvY2kgZnJvbSB0aGUgZGF0YSBzZXQsIHRob3VnaCBpdCBpcyBpbXBvcnRhbnQgdG8gZmxhZyBwb3RlbnRpYWxseSBwcm9ibGVtYXRpYyBsb2NpIGJhc2VkIG9uIHRoZWlyIGhhcGxvdHlwaW5nIHN0YXRzIGF0IHRoaXMgcG9pbnQuCgpgYGB7ciBmaWx0ZXIgc3VjY2VzcyBoYXBsb3R5cGVkIGluZHZ9CgojIGNvdW50IGluZGl2aWR1YWxzIGFib3ZlIHNldCB0aHJlc2hvbGQKY291bnQoaGFwX2luZF9zdGF0cywgUHJvcF9TdWNjZXNzIDwgLjkpCgojIGNyZWF0ZSB2ZWN0b3Igb2YgaW5kdiB0byByZW1vdmUgKGNob29zZSBjdXQtb2ZmKQpQcm9wU3VjY2Vzc0luZCA8LSBmaWx0ZXIuaW5kLnByb3Bfc3VjY2VzcyhoYXBfaW5kX3N0YXRzLCAuOSkKCndyaXRlLnRhYmxlKFByb3BTdWNjZXNzSW5kLCAiZGF0YS9QT1BHRU4vcHJvcFN1Y2MuaW5kdiIsIAogICAgICAgICAgICBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSwgc2VwID0gIlx0IikKCiMgVmlldyhhcy5kYXRhLmZyYW1lKFByb3BTdWNjZXNzSW5kKSkKCmBgYAoKKipQb3NzaWJsZSBwYXJhbG9ncyBwZXIgaW5kaXZpZHVhbCoqCgpJbmRpdmlkdWFscyB3aXRoIGhpZ2ggcHJvcG9ydGlvbiBvZiBsb2NpIGZsYWdnZWQgYXMgcG9zc2libGUgcGFyYWxvZ3Mgc2hvdWxkIGJlIGZsYWdnZWQgYXMgcG90ZW50aWFsIHByb2JsZW0gaW5kaXZpZHVhbHMuIAoKYGBge3IgcG9zcyBwYXJhbG9ncyBwZXIgaW5kdiwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9NSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCmdncGxvdChoYXBfaW5kX3N0YXRzLCBhZXMoeCA9IFBvc3NfUGFyYWxvZ3MpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMCwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oUG9zc19QYXJhbG9ncywgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya3JlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IChucm93KGhhcF9zdGF0cykqMC4wMSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAibm8gb2YgbG9jaSBwb3RlbnRpYWwgcGFyYWxvZ3MiKSArCiAgdGhlbWVfc3RhbmRhcmQKCmBgYAoKQ3V0LW9mZiBmb3IgaW5kaXZpZHVhbHMgd2l0aCBsb2NpIGZsYWdnZWQgcGFyYWxvZ3MgaW4gPiA1JSBvZiBsb2NpIGlzIGByIG5yb3coaGFwX3N0YXRzKSowLjA1YC4KCmBgYHtyIGZpbHRlciBwYXJhbG9ncyBpbmR2fQoKIyBjb3VudCBpbmRpdmlkdWFscyBhYm92ZSBzZXQgdGhyZXNob2xkCmNvdW50KGhhcF9pbmRfc3RhdHMsIFBvc3NfUGFyYWxvZ3MgPiAxNTApCgojIGNyZWF0ZSB2ZWN0b3Igb2YgaW5kdiB0byByZW1vdmUgKGNob29zZSBjdXQtb2ZmKQpQb3NzX1BhcmFsb2dJbmQgPC0gZmlsdGVyLmluZC5wYXJhbG9ncyhoYXBfaW5kX3N0YXRzLCAxNTApCgp3cml0ZS50YWJsZShQb3NzX1BhcmFsb2dJbmQsICJkYXRhL1BPUEdFTi9wb3NzUGFyYWwuaW5kdiIsIAogICAgICAgICAgICBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSwgc2VwID0gIlx0IikKCiMgVmlldyhhcy5kYXRhLmZyYW1lKFBvc3NfUGFyYWxvZ0luZCkpCgpgYGAKClRoZSBoaWdoZXN0IG51bWJlciBvZiBmbGFnZ2VkIGxvY2kgaW4gYW4gaW5kaXZpZHVhbHMgaXMgYHIgbWF4KGhhcF9pbmRfc3RhdHMkUG9zc19QYXJhbG9ncywgbmEucm0gPSBUUlVFKWAsIHdoaWNoIGlzIGVxdWl2YWxlbnQgdG8gYHIgcm91bmQobWF4KGhhcF9pbmRfc3RhdHMkUG9zc19QYXJhbG9ncywgbmEucm0gPSBUUlVFKS9ucm93KGhhcF9zdGF0cyksIGRpZ2l0cyA9IDQpKjEwMGAlIG9mIGxvY2kuCgoqKkluZGl2aWR1YWxzIHdpdGggaGlnaCBwcm9wb3J0aW9uIG9mIHBvdGVudGlhbCBhbGxlbGUgZHJvcG91dC9nZW5vdHlwaW5nIGVycm9yKioKCmBgYHtyIGdlbm90eXBnIGVycm9yIHBlciBpbmR2LCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD00LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKZ2dwbG90KGhhcF9pbmRfc3RhdHMsIGFlcyh4ID0gTG93X0NvdmVyYWdlLkVycm9ycykpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKExvd19Db3ZlcmFnZS5FcnJvcnMsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAobnJvdyhoYXBfc3RhdHMpKjAuMDEpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIiMgbG9jaSBwb3RlbnRpYWwgZ2Vub3R5cGluZyBlcnJvciIsIHkgPSAiIyBpbmR2IikgKwogIHRoZW1lX3N0YW5kYXJkCgpgYGAKClJlbW92ZSBpbmRpdmlkdWFscyB3aXRoIGhpZ2ggcHJvcG9ydGlvbiBvZiBsb2NpIHRoYXQgaGF2ZSBiZWVuIGZsYWdnZWQgYXMgcG90ZW50aWFsIGFsbGVsZSBkcm9wb3V0cy9nZW5vdHlwaW5nIGVycm9yLgoKYGBge3IgZmlsdGVyIGdlbm90eXBpbmcgZXJyb3IgaW5kdn0KCiMgY291bnQgaW5kaXZpZHVhbHMgYWJvdmUgc2V0IHRocmVzaG9sZApjb3VudChoYXBfaW5kX3N0YXRzLCBMb3dfQ292ZXJhZ2UuRXJyb3JzID4gMTUwKQoKIyBjcmVhdGUgdmVjdG9yIG9mIGluZHYgdG8gcmVtb3ZlIChjaG9vc2UgY3V0LW9mZikKR2Vub0VyckluZHYgPC0gZmlsdGVyLmluZC5nZW5vX2VycihoYXBfaW5kX3N0YXRzLCAxNTApCgp3cml0ZS50YWJsZShHZW5vRXJySW5kdiwgImRhdGEvUE9QR0VOL0dlbm9FcnIuaW5kdiIsIAogICAgICAgICAgICBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSwgc2VwID0gIlx0IikKCiMgVmlldyhHZW5vRXJySW5kdikKCmBgYAoKVGhlIGhpZ2hlc3QgbnVtYmVyIG9mIGZsYWdnZWQgbG9jaSBpbiBhbiBpbmRpdmlkdWFscyBpcyBgciBtYXgoaGFwX2luZF9zdGF0cyRMb3dfQ292ZXJhZ2UuRXJyb3JzLCBuYS5ybSA9IFRSVUUpYCwgd2hpY2ggaXMgZXF1aXZhbGVudCB0byBgciByb3VuZChtYXgoaGFwX2luZF9zdGF0cyRMb3dfQ292ZXJhZ2UuRXJyb3JzLCBuYS5ybSA9IFRSVUUpL25yb3coaGFwX3N0YXRzKSwgZGlnaXRzID0gNCkqMTAwYCUgb2YgbG9jaS4KCiMjIEZpbHRlciBmbGFnZ2VkIGxvY2kgYW5kIGluZGl2aWR1YWxzIGFzIG5lY2Vzc2FyeQoKTG9hZCBnZW5lcG9wIGZpbGUgd2l0aCBoYXBsb3R5cGVkIGxvY2kuCgpgYGB7ciBsb2FkIGdlbmVwb3AsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIGltcG9ydCBnZW5lcG9wIGZpbGUKZ2VuIDwtIHJlYWQuZ2VuZXBvcChmaWxlID0gImRhdGEvSGFwbG90eXBpbmcvQ0xFLmdlbiIsCiAgICAgICAgICAgICAgICAgICAgbmNvZGUgPSAzTCwgcXVpZXQgPSBGQUxTRSkKCmdlbgoKYGBgCgpSZW1vdmUgZmxhZ2dlZCBsb2NpIGFuZCBpbmRpdmlkdWFscy4KCmBgYHtyIHJlbW92ZSBsb2NpIGluZGl2aWR1YWxzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKIyByZW1vdmUgZmxhZ2dlZCBsb2NpCnJlbW92ZWxvYyA8LSBjKFBvc3NfUGFyYWxvZ3MsIE5vU05wcywgTm9IYXBzLCBHZW5vX0VyciwgUHJvcF9IYXBsb3R5cGVkKQoKZ2VuIDwtIGdlbmluZC5yZW0ubG9jaShnZW4sIHJlbW92ZWxvYykKCiMgcmVtb3ZlIGZsYWdnZWQgaW5kaXZpZHVhbHMKcmVtb3ZlSW5kIDwtIGMoUG9zc19QYXJhbG9nSW5kLCBQcm9wU3VjY2Vzc0luZCwgR2Vub0VyckluZHYpCmdlbiA8LSBnZW4uaW5kLnJlbS5JbmQoZ2VuLCByZW1vdmVJbmQpCgpnZW4KCmBgYAoKIyBDb21wYXJlIGR1cGxpY2F0ZSBpbmRpdmlkdWFscwoKYGBge3IgY29tcCBkdWxwbGljYXRlIGluZHYsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIGV4dHJhY3QgZ2Vub3R5cGUgbWF0cml4CmRmIDwtIGdlbmluZDJkZihnZW4sCiAgICAgICAgICAgICAgICB1c2Vwb3AgPSBUUlVFLAogICAgICAgICAgICAgICAgc2VwID0gIjoiLCBvbmVDb2xQZXJBbGwgPSBGQUxTRSkgJT4lCiAgc2VsZWN0KC1wb3ApICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigpCgojIGNyZWF0ZSBsaXN0IG9mIGR1cGxpY2F0ZXMKcGFpcnMgPC0gbGlzdCgpCgojIGlucHV0IGVhY2ggc2V0IG9mIGR1cGxpY2F0ZXMgYXMgYSB2ZWN0b3Igb2YgYSBsaXN0CnBhaXJzW1sxXV0gPC0gYygiQ0xFXzJBLUEwMl9DbGV1X0FyYTAwMiIsICJDTEVfM0EtRzA3X0NsZXVfQXJhMDAyIikKcGFpcnNbWzJdXSA8LSBjKCJDTEVfMkEtQTEwX0NsZXVfQXJhMDEwIiwgIkNMRV8yQS1DMDdfQ2xldV9BcmEwMTAiKQpwYWlyc1tbM11dIDwtIGMoIkNMRV8yQS1CMTFfQ2xldV9NYXQwMDEiLCAiQ0xFXzNBLUMwN19DbGV1X01hdDAwMSIpCnBhaXJzW1s0XV0gPC0gYygiQ0xFXzJBLUMwOF9DbGV1X0FyYTA5MSIsICJDTEVfMkItQTAxX0NsZXVfQXJhMDkxIikKcGFpcnNbWzVdXSA8LSBjKCJDTEVfMkEtRjAyX0NsZXVfU2FuMDA0IiwgIkNMRV8zQi1DMDdfQ2xldV9TYW4wMDQiKQpwYWlyc1tbNl1dIDwtIGMoIkNMRV8yQS1HMDdfQ2xldV9EaWMwMTMiLCAiQ0xFXzNBLUIwM19DbGV1X0RpYzAxMyIpCnBhaXJzW1s3XV0gPC0gYygiQ0xFXzJBLUcwOF9DbGV1X0FyYTA2NCIsICJDTEVfM0ItQTAzX0NsZXVfQXJhMDY0IikKcGFpcnNbWzhdXSA8LSBjKCJDTEVfMkItQjA1X0NsZXVfRGljMDcwIiwgIkNMRV8zQi1DMDhfQ2xldV9EaWMwNzAiKQpwYWlyc1tbOV1dIDwtIGMoIkNMRV8yQi1CMTBfQ2xldV9NYXQwMzAiLCAiQ0xFXzNBLUcwOF9DbGV1X01hdDAzMCIpCnBhaXJzW1sxMF1dIDwtIGMoIkNMRV8yQi1GMDJfQ2xldV9BcmEwODIiLCAiQ0xFXzNBLUMwOF9DbGV1X0FyYTA4MiIpCnBhaXJzW1sxMV1dIDwtIGMoIkNMRV8yQi1HMDdfQ2xldV9EaWMwMTgiLCAiQ0xFXzNBLUIwOF9DbGV1X0RpYzAxOCIpCnBhaXJzW1sxMl1dIDwtIGMoIkNMRV8yQi1HMDhfQ2xldV9NYXQwMjMiLCAiQ0xFXzNCLUMwM19DbGV1X01hdDAyMyIpCnBhaXJzW1sxM11dIDwtIGMoIkNMRV8zQi1HMDRfQ2xldV9EaWMwNDkiLCAiQ0xFXzNCLUcwOF9DbGV1X0RpYzA0OSIpCnBhaXJzW1sxNF1dIDwtIGMoIkNMRV8yQS1CMDRfQ2xldV9EaWMwMDYiLCAiQ0xFXzNBLUYwNl9DbGV1X0FyYTA1NSIpCnBhaXJzW1sxNV1dIDwtIGMoIkNMRV8yQS1FMDZfQ2xldV9TYWIwMDYiLCAiQ0xFXzNBLUUwNV9DbGV1X1NhYjAxNSIpCnBhaXJzW1sxNl1dIDwtIGMoIkNMRV8zQi1GMDVfQ2xldV9EaWMwMzgiLCAiQ0xFXzNCLUYwOV9DbGV1X0RpYzA0MiIpCnBhaXJzW1sxN11dIDwtIGMoIkNMRV8yQS1CMDJfQ2xldV9EaWMwMDQiLCAiQ0xFXzNBLUYwNF9DbGV1X0FyYTA1MyIpCnBhaXJzW1sxOF1dIDwtIGMoIkNMRV8yQS1FMDVfQ2xldV9TYWIwMDUiLCAiQ0xFXzNBLUUwNF9DbGV1X1NhYjAxNCIpCnBhaXJzW1sxOV1dIDwtIGMoIkNMRV8yQS1BMTFfQ2xldV9EaWMwMDEiLCAiQ0xFXzNCLUIxMV9DbGV1X0RpYzAzMyIpCnBhaXJzW1syMF1dIDwtIGMoIkNMRV8yQS1CMDZfQ2xldV9EaWMwMDgiLCAiQ0xFXzNBLUYwN19DbGV1X0FyYTA1NiIpCnBhaXJzW1syMV1dIDwtIGMoIkNMRV8yQS1CMDVfQ2xldV9EaWMwMDciLCAiQ0xFXzNBLUYwNV9DbGV1X0FyYTA1NCIpCnBhaXJzW1syMl1dIDwtIGMoIkNMRV8yQS1FMDNfQ2xldV9TYWIwMDMiLCAiQ0xFXzNBLUUwMl9DbGV1X1NhYjAxMiIpCnBhaXJzW1syM11dIDwtIGMoIkNMRV8yQS1CMDFfQ2xldV9EaWMwMDMiLCAiQ0xFXzNBLUYwM19DbGV1X0FyYTA1MiIpCnBhaXJzW1syNF1dIDwtIGMoIkNMRV8yQS1CMDdfQ2xldV9EaWMwMDkiLCAiQ0xFXzNBLUYwOF9DbGV1X0FyYTA1NyIpCnBhaXJzW1syNV1dIDwtIGMoIkNMRV8yQS1FMDJfQ2xldV9TYWIwMDIiLCAiQ0xFXzNBLUUwMV9DbGV1X1NhYjAxMSIpCnBhaXJzW1syNl1dIDwtIGMoIkNMRV8yQS1FMDFfQ2xldV9TYWIwMDEiLCAiQ0xFXzNCLUUxMl9DbGV1X1NhYjAzNCIpCnBhaXJzW1syN11dIDwtIGMoIkNMRV8yQS1FMDhfQ2xldV9TYWIwMDgiLCAiQ0xFXzNBLUUwN19DbGV1X1NhYjAxNyIpCnBhaXJzW1syOF1dIDwtIGMoIkNMRV8yQS1CMDhfQ2xldV9EaWMwMTAiLCAiQ0xFXzNBLUYwOV9DbGV1X0FyYTA1OCIpCnBhaXJzW1syOV1dIDwtIGMoIkNMRV8yQS1FMDdfQ2xldV9TYWIwMDciLCAiQ0xFXzNBLUUwNl9DbGV1X1NhYjAxNiIpCnBhaXJzW1szMF1dIDwtIGMoIkNMRV8yQS1GMDlfQ2xldV9BcmEwMjEiLCAiQ0xFXzJBLUcwNV9DbGV1X0FyYTAyOSIpCgojIGNyZWF0ZSBlbXB0eSBsaXN0IGZvciBnZW5vdHlwZSBlcnJvciAoZGlzY29yZGFudCBsb2NpKQpnZW5vZXJyb3IgPC0gbGlzdCgpCgojIGNyZWF0ZSBlbXB0eSB2ZWN0b3IgZm9yIGR1cGxpY2F0ZXMgKHJldGFpbnMgZmlyc3QgaW5kdiBpbiBwYWlyKQpkdXAgPC0gY2hhcmFjdGVyKCkKCiMgaWRlbnRpZnkgZGlzY29yZGFudCBnZW5vdHlwZXMgZm9yIGVhY2ggc2V0IG9mIGR1cGxpY2F0ZXMKZm9yIChwIGluIHBhaXJzKXsKCiMgc2VsZWN0IGR1cGxpY2F0ZXMgZnJvbSBnZW5vdHlwZSBtYXRyaXgKZ2VubyA8LSBkZiAlPiUKICBmaWx0ZXIocm93bmFtZSAlaW4lIHApICU+JQogIHNlbGVjdCgtcm93bmFtZSkKCiMgY29tcGFyZSBnZW5vdHlwZXMKY29tcCA8LSAodChnZW5vKSkKCmNvbnRpZ3MgPC0gYXMuZGF0YS5mcmFtZShjb21wKSAlPiUKICByb3duYW1lc190b19jb2x1bW4odmFyID0gIkxPQ1VTIikgJT4lCiAgbXV0YXRlKFYxID0gYXMuY2hhcmFjdGVyKFYxKSwKICAgICAgICAgVjIgPSBhcy5jaGFyYWN0ZXIoVjIpKSAlPiUKICBmaWx0ZXIoVjEgIT0gVjIpCgojIHdyaXRlIHZlY3RvciB3aXRoIGZpcnN0IGluZGl2aWR1YWwgaW4gcGFpcgppbmQgPC0gcFsxXQoKZHVwIDwtIGMoZHVwLCBpbmQpCgpnZW5vZXJyb3JbW2luZF1dIDwtIGNvbnRpZ3MKCn0KCiMgaWYgaXQgdGhyb3dzIGVycm9yIG9iamVjdCBWMSBvciBWMiBub3QgZm91bmQgaXQgbWVhbnMgb25lIG9yIG1vcmUgb2YgdGhlIHNhbXBsZXMgbmFtZXMgYXJlIG5vdCBjb3JyZWN0CgojIGNvbWJpbmUgaW50byBvbmUgZGF0YWZyYW1lCmdlbm9lcnJvciA8LSBsZHBseShnZW5vZXJyb3IsIGRhdGEuZnJhbWUpICU+JQogIHJlbmFtZShEVVAxID0gYC5pZGAsCiAgICAgICAgIEdFTk9fSU5EVjEgPSBWMSwKICAgICAgICAgR0VOT19JTkRWMiA9IFYyKQoKd3JpdGVfZGVsaW0oZ2Vub2Vycm9yLCAicmVzdWx0cy9oYXBsb3R5cGVkLmdlbm9lcnJvciIsIGRlbGltID0gIlx0IikKCmBgYAoKQ29tcGFyZSBnZW5vdHlwZSBkZXB0aCBmb3IgZGlzY29yZGFudCBsb2NpOgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiMgaW1wb3J0IGdlbm90eXBlIGRlcHRoIGtlZXAgb25lIGRlcHRoIHBlciBjb250aWcKbiA8LSBucm93KGluZF9zdGF0c19GMTQpICsgMQoKZ2RlcHRoIDwtIHJlYWRfdGFibGUyKCJkYXRhL1ZDRi9DTEUuRjE0LmdkZXB0aCIpICU+JQogIHNlbGVjdCgtUE9TKSAlPiUKICBkaXN0aW5jdChDSFJPTSwgLmtlZXBfYWxsID0gVFJVRSkgJT4lCiAgZ2F0aGVyKGtleSA9IElORFYsIHZhbHVlID0gR0RFUFRILCAyOm4pICU+JQogIHJlbmFtZShMT0NVUyA9IENIUk9NKQoKIyBjcmVhdGUgZGF0YWZyYW1lIHdpdGggZHVwbGljYXRlcwpkdXBfaW5kdiA8LSBkYXRhLmZyYW1lKG1hdHJpeChuY29sID0gMiwgbnJvdyA9IDApKQpjb2xuYW1lcyhkdXBfaW5kdikgPC0gYygiRFVQMSIsICJEVVAyIikKCmZvciAocCBpbiBwYWlycyl7CgpwIDwtIGFzLmRhdGEuZnJhbWUodChwKSkKCmNvbG5hbWVzKHApIDwtICBjKCJEVVAxIiwgIkRVUDIiKQoKZHVwX2luZHYgPC0gYmluZF9yb3dzKGR1cF9pbmR2LCBwKQogICAKfQoKIyBhZGQgZHVwbGljYXRlIDIgdG8gZ2Vub2Vycm9yIGRhdGEgZnJhbWUKZ2Vub2Vycm9yIDwtIGxlZnRfam9pbihnZW5vZXJyb3IsIGR1cF9pbmR2KQoKIyBhZGQgZ2Vub3R5cGUgZGVwdGggaW5mb3JtYXRpb24gZm9yIGR1cGxpY2F0ZSAxCmdkZXB0aCA8LSBnZGVwdGggJT4lCiAgcmVuYW1lKERVUDEgPSBJTkRWKQoKZ2Vub2Vycm9yIDwtIGxlZnRfam9pbihnZW5vZXJyb3IsIGdkZXB0aCkgJT4lCiAgcmVuYW1lKEdERVBUSDEgPSBHREVQVEgpCgojIGFkZCBnZW5vdHlwZSBkZXB0aCBpbmZvcm1hdGlvbiBmb3IgZHVwbGljYXRlIDIKZ2RlcHRoIDwtIGdkZXB0aCAlPiUKICByZW5hbWUoRFVQMiA9IERVUDEpCgpnZW5vZXJyb3IgPC0gbGVmdF9qb2luKGdlbm9lcnJvciwgZ2RlcHRoKSAlPiUKICByZW5hbWUoR0RFUFRIMiA9IEdERVBUSCkKCiMgaWRlbnRpZnkgaG9tb3p5Z290ZSB2cyBoZXRlcm96eWdvdGUgZ2Vub3R5cGVzCmdlbm9lcnJvciA8LSBnZW5vZXJyb3IgJT4lCiAgc2VwYXJhdGUoY29sID0gR0VOT19JTkRWMSwgaW50byA9IGMoIkdFTk8xYSIsICJHRU5PMWIiKSwgc2VwID0gIjoiLCByZW1vdmUgPSBGQUxTRSkgJT4lCiAgc2VwYXJhdGUoY29sID0gR0VOT19JTkRWMiwgaW50byA9IGMoIkdFTk8yYSIsICJHRU5PMmIiKSwgc2VwID0gIjoiLCByZW1vdmUgPSBGQUxTRSkgJT4lCiAgbXV0YXRlKEdFTk9UWVBFMSA9IGlmZWxzZShHRU5PMWEgPT0gR0VOTzFiLCAiSE9NT1pZR09URSIsICJIRVRFUk9aWUdPVEUiKSwKICAgICAgICAgR0VOT1RZUEUyID0gaWZlbHNlKEdFTk8yYSA9PSBHRU5PMmIsICJIT01PWllHT1RFIiwgIkhFVEVST1pZR09URSIpKQoKYGBgCgpEZXRlcm1pbmUgbnVtYmVyIG9mIHRpbWVzIGRpc2NvcmRhbnQgZ2Vub3R5cGUgaXMgZHVlIHRvIGhldGVyb3p5Z290ZS9ob21venlnb3RlLgoKYGBge3J9Cgpjb3VudChnZW5vZXJyb3IsIEdFTk9UWVBFMSA9PSBHRU5PVFlQRTIpCgpgYGAKCkNvbXBhcmUgbnVtYmVyIG9mIGRpc2NvcmRhbnQgZ2Vub3R5cGUgY2FsbHMgcGVyIGR1cGxpY2F0ZSBzZXQuCgpgYGB7ciwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NH0KCnRvdGFsIDwtIGFzLm51bWVyaWMobGVuZ3RoKGxvY05hbWVzKGdlbikpKQoKIyBjb21wYXIgbnVtYmVyIG9mIGxvY2kgYnkgcGFpcgpwZXJfaW5kIDwtIGNvdW50KGdlbm9lcnJvciwgRFVQMSkgJT4lCiAgbXV0YXRlKGZyZXEgPSBuL3RvdGFsKjEwMCkKCmdncGxvdChwZXJfaW5kLCBhZXMoZnJlcSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMjUsIGZpbGwgPSAiZGFya29yYW5nZSIsIGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKGZyZXEsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgY29sb3IgPSAiZGFya3JlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIiUgZ2Vub3R5cGluZyBlcnJvciBwZXIgaW5kdiIpICsKICB0aGVtZV9zdGFuZGFyZAoKYGBgCgpJZGVudGlmeSBsb2NpIGNvbnNpc3RlbnRseSBhZmZlY3RlZCBieSBnZW5vdHlwaW5nIGVycm9yCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD00LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKdG90YWwgPC0gYXMubnVtZXJpYyhsZW5ndGgocGFpcnMpKQoKIyBjb21wYXJlIGxvY2kgYWZmZWN0ZWQgYnkgZ2Vub3R5cGluZyBlcnJvcgpwZXJfbG9jIDwtIGNvdW50KGdlbm9lcnJvciwgTE9DVVMpICU+JQogIG11dGF0ZShmcmVxID0gbi90b3RhbCoxMDApICU+JQogIHJlbmFtZShMb2N1cyA9IExPQ1VTKQoKcGVyX2xvYyA8LSBsZWZ0X2pvaW4ocGVyX2xvYywgaGFwX3N0YXRzKQoKZ2dwbG90KHBlcl9sb2MsIGFlcyh4ID0gbikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIGZpbGwgPSAiZGFya29yYW5nZSIsIGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKG4sIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgY29sb3IgPSAiZGFya3JlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gInNhbWUgbG9jdXMgYWZmZWN0ZWQgaW4gbiBpbmR2IikgKwogIHRoZW1lX3N0YW5kYXJkCgpgYGAKCmBgYHtyfQoKY291bnQocGVyX2xvYywgbiA+IDMpCgpgYGAKClJlbW92ZSBmbGFnZ2VkIGxvY2kgYW5kIG9uZSBpbmRpdmlkdWFsIHBlciBwYWlyLgoKYGBge3J9CgojIHJlbW92ZSBkdXBsaWNhdGVzCnJlbW92ZUluZCA8LSBkdXAKCmdlbiA8LSBnZW4uaW5kLnJlbS5JbmQoZ2VuLCByZW1vdmVJbmQpCgojIHJlbW92ZSBmbGFnZ2VkIGxvY2kKcmVtb3ZlbG9jIDwtIGZpbHRlcihwZXJfbG9jLCBuID4gMykKZ2VuIDwtIGdlbmluZC5yZW0ubG9jaShnZW4sIHJlbW92ZWxvYykKCmdlbgoKYGBgCgpgciBucm93KHRlbXApYCBsb2NpIHdlcmUgcmVtb3ZlZCBhcyBwb3RlbnRpYWxseSBhZmZlY3RlZCBieSBnZW5vdHlwaW5nIGVycm9yLgoKIyBSZWxhdGVkbmVzcyAmIEluYnJlZWRpbmcKCkNyZWF0ZSBpbnB1dCBmaWxlIGZvcmUgYHJlbGF0ZWRgIHBhY2thZ2UuCgpgYGB7cn0KCiMgbmVlZCB0byBjaGFuZ2UgdG8gUiAzMgpsaWJyYXJ5KHJlbGF0ZWQpCgojIGNyZWF0ZSBpbnB1dCBmaWxlIC0tLS0KZGYgPC0gZ2VuaW5kMmRmKGdlbiwgdXNlcG9wID0gRkFMU0UsIG9uZUNvbFBlckFsbCA9IFRSVUUpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigiSU5EViIpICU+JQogIHNlcGFyYXRlKElORFYsIGludG8gPSBjKCJQT1AiLCAiTElCIiwgIklORFYiKSwgc2VwID0gIl8iLCBleHRyYSA9ICJtZXJnZSIpICU+JQogIHNlbGVjdCgtUE9QKSAlPiUKICB1bml0ZShJTkQsIExJQiwgSU5EViwgc2VwID0gIl8iKQoKIyBtaXNzaW5nIGRhdGEgbXVzdCBiZSAwCmRmW2RmPT0iTkEiXSA8LSAwCgp3cml0ZS50YWJsZShkZiwgInNjcmF0Y2gvcmVsYXRlZC5pbnB1dCIsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFLCBjb2wubmFtZXMgPSBGQUxTRSwgc2VwID0gIlx0IiwgcXVvdGUgPSBGQUxTRSkKCiMgaW1wb3J0IGlucHV0IGZpbGUgYXMgbGlzdCAoZ2RhdGEsIG5sb2NpLCBuYWxsZWxlcywgbmluZHMsIGZyZXFzKQpnZW5vdHlwZWRhdGEgPC0gcmVhZGdlbm90eXBlZGF0YSgic2NyYXRjaC9yZWxhdGVkLmlucHV0IikKCmBgYAoKQ2FsY3VsYXRlIHBhaXJ3aXNlIHJlbGF0ZWRuZXNzIGFjY29yZGluZyB0byBMeW5jaCAmIFJpdGxhbmQgMTk5OTsgdXNlcyBpbmJyZWVkaW5nIGVzdGltYXRlcyBmb3IgZWFjaCBpbmRpdmlkdWFscyB0byBlc3RpbWF0ZSByZWxhdGVkbmVzcy4KCmBgYHtyIGx5bmNocmQsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTd9CgojIHBhaXJ3aXNlIHJlbGF0ZWRuZXNzIChMeW5jaCAmIFJpdGxhbmQgMTk5OSkKcmVsYXRlZG5lc3NfbHluY2hyZCA8LSBjb2FuY2VzdHJ5KGdlbm90eXBlZGF0YSRnZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGx5bmNocmQgPSAxKQoKcmVsYXRlZG4gPC0gcmVsYXRlZG5lc3NfbHluY2hyZCRyZWxhdGVkbmVzcyAlPiUKICBzZWxlY3QocGFpci5ubywgaW5kMS5pZCwgaW5kMi5pZCwgbHluY2hyZCkKCndyaXRlX2RlbGltKHJlbGF0ZWRuLCAicmVzdWx0cy9wYWlyd2lzZV9yZWxhdGVkbmVzcyIpCgpWaWV3KGluZF9zdGF0c19GMTQpCgppbmJyZWVkIDwtIHJlbGF0ZWRuZXNzX2x5bmNocmQkaW5icmVlZGluZyAlPiUKICBzZWxlY3QoaW5kLmlkLCBMMywgTEgpICU+JQogIHJlbmFtZShJTkRWID0gaW5kLmlkKQoKd3JpdGVfZGVsaW0oaW5icmVlZCwgInJlc3VsdHMvaW5icmVlZGluZyIpCgpgYGAKCkRpc3RyaWJ1dGlvbiBvZiBsZXZlbHMgb2YgcGFpcndpc2UgcmVsYXRlZG5lc3M6CgpgYGB7ciBseW5jaHJkIGRpc3RyaWIgcmVsYXRlZG4sIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTh9CgpnZ3Bsb3QocmVsYXRlZG4sIGFlcyh4ID0gbHluY2hyZCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMDEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKGx5bmNocmQsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBxdWFudGlsZShseW5jaHJkLCAwLjk1KSksCiAgICAgICAgICAgICBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBzY2FsZV95X3NxcnQoKSArCiAgbGFicyh4ID0gInJlbGF0ZWRuZXNzIiwgeSA9ICJudW1iZXIgb2YgcGFpcnMiKSArCiAgdGhlbWVfc3RhbmRhcmQKCmBgYAoKRGlzdHJpYnV0aW9uIG9mIGxldmVscyBvZiBob21venlnb3NpdHk6CgpgYGB7ciBseW5jaHJkIGluYnJlZWRpbmcsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTR9CgpnZ3Bsb3QoaW5icmVlZCwgYWVzKHggPSBMMykpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMDA1LCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihMSCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IHF1YW50aWxlKExILCAwLjk1KSksCiAgICAgICAgICAgICBjb2xvciA9ICJkYXJrcmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAiaW5icmVlZGluZyBjb2VmZmljaWVudCIsIHkgPSAiaW5kaXZpZHVhbHMiKSArCiAgdGhlbWVfc3RhbmRhcmQKCmBgYAoKIyBIZXRlcm96eWdvc2l0eSBhbmQgSFdFCgpHZW5lcmF0ZSBzdW1tYXJ5IHN0YXRpc3RpY3MKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKU2FtcGxlSW5mbyA8LSByZWFkX2RlbGltKCJkYXRhL1BPUEdFTi9TYW1wbGVJbmZvLnR4dCIsIGRlbGltID0gIlx0IikgJT4lCiAgZGlzdGluY3QoU0FNUExFX0lELCAua2VlcF9hbGwgPSBUUlVFKQoKSW5kcyA8LSBhcy5kYXRhLmZyYW1lKGluZE5hbWVzKGdlbikpICU+JQogIHJlbmFtZShMSUJfSUQgPSBgaW5kTmFtZXMoZ2VuKWApICU+JQogIHNlcGFyYXRlKExJQl9JRCwgaW50byA9IGMoIlNQIiwgIldFTEwiLCAiU0FNUExFX0lEIiksIHNlcCA9ICJfIiwgZXh0cmEgPSAibWVyZ2UiLCByZW1vdmUgPSBGQUxTRSkKCnN0cmF0YSA8LSBsZWZ0X2pvaW4oSW5kcywgU2FtcGxlSW5mbywgYnkgPSAiU0FNUExFX0lEIikgJT4lCiAgZGlzdGluY3QoKQoKc3RyYXRhKGdlbikgPC0gc3RyYXRhCgojIGRlZmluZSBncm91cHMgdXNpbmcgc3RyYXRhIGluZm9ybWF0aW9uIC0gbmVlZCB0byBhZGQgUE9QIGZvciBtaXNzaW5nIG9uZXMKc2V0UG9wKGdlbikgPC0gflBPUAoKcG9wTmFtZXMoZ2VuKQoKIyBnZW5lcmF0ZSBnZW5ldGljIGRpdmVyc2l0eSBzdGF0cwpHZW5EaXYgPC0gYWRlZ2VuZXQ6OnN1bW1hcnkoZ2VuKQoKZGF0IDwtIGhpZXJmc3RhdDo6Oi5nZW5pbmQyaGllcmZzdGF0KGdlbikKR2VuRGl2MiA8LSBiYXNpYy5zdGF0cyhkYXQpCgpgYGAKCkNvbXBhcmUgb2JzZXJ2ZWQgKEhvKSBhbmQgZXhwZWN0ZWQgKEhlKSBoZXRlcm96eWdvc2l0eSBmb3IgYWxsIGluZGl2aWR1YWxzIGFjcm9zcyBhbGwgcG9wdWxhdGlvbnMuCgpgYGB7cn0KCiMgb2JzZXJ2ZWQgaGV0ZXJvenlnb3NpdHkgcGVyIGxvY3VzCkhvIDwtIGFzLmRhdGEuZnJhbWUoR2VuRGl2JEhvYnMpICU+JSAKICByb3duYW1lc190b19jb2x1bW4oIkxPQ1VTIikgJT4lCiAgcmVuYW1lKEhvID0gYEdlbkRpdiRIb2JzYCkKCiMgZXhwZWN0ZWQgaGV0ZXJvenlnb3NpdHkgcGVyIGxvY3VzCkhzIDwtIGFzLmRhdGEuZnJhbWUoR2VuRGl2JEhleHApICU+JSAKICByb3duYW1lc190b19jb2x1bW4oIkxPQ1VTIikgJT4lCiAgcmVuYW1lKEhleHAgPSBgR2VuRGl2JEhleHBgKQoKIyBleHBlY3RlZCBhbmQgb2JzZXJ2ZWQgaGV0ZXJvenlzaXR5IHBlciBsb2N1cwpIZXQgPC0gbGVmdF9qb2luKEhvLCBIcywgYnkgPSAiTE9DVVMiKQoKYGBgCgpJZGVudGlmeSBsb2NpIHRoYXQgYXJlIG5vdyBtb25vbW9ycGhpYyBhbmQgcmVtb3ZlIGZyb20gZGF0YSBzZXQ6CgpgYGB7cn0KCm1vbm9tb3JwaGljIDwtIGZpbHRlcihIbywgSG8gPT0gMCkKClZpZXcobW9ub21vcnBoaWMpCgptb25vbW9ycGhpYyA8LSBtb25vbW9ycGhpYyRMT0NVUwoKIyByZW1vdmUgZmxhZ2dlZCBsb2NpCnJlbW92ZWxvYyA8LSBjKG1vbm9tb3JwaGljKQpnZW4gPC0gZ2VuaW5kLnJlbS5sb2NpKGdlbiwgcmVtb3ZlbG9jKQoKZ2VuCgpgYGAKCkV4cGVjdGVkIHZzIG9ic2VydmVkIGhldGVyb3p5Z29zaXR5IGFjcm9zcyBhbGwgcG9wdWxhdGlvbnMuCgpgYGB7ciBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD00fQoKdGVtcCA8LSBIZXQgJT4lCiAgZmlsdGVyKEhvICE9IDApCgojIHBsb3QgSG8gdnMgSHMgYWNyb3NzIGFsbCBpbmRpdmlkdWFscwpnZ3Bsb3QodGVtcCwgYWVzKHggPSBIbywgeSA9IEhleHApKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEsIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgeGxpbSgwLCAxKSArCiAgeWxpbSgwLCAxKSArCiAgbGFicyh4ID0gIm9ic2VydmVkIGhldGVyb3p5Z29zaXR5IEhvIiwgeSA9ICJ3aXRoaW4gY2x1c3RlciBkaXZlcnNpdHkgSHMgKEhleHApIikgKwogIHRoZW1lX3N0YW5kYXJkCgpgYGAKCkNvbXBhcmUgZGlzdHJpYnV0aW9uIG9mIG9ic2VydmVkIGhldGVyb3p5Z29zaXR5IChIbykgaW4gZWFjaCBwdXRhdGl2ZSBwb3B1bGF0aW9uOgoKYGBge3J9CgpIb19wZXIgPC0gYXMuZGF0YS5mcmFtZShHZW5EaXYyJEhvKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oIkxPQ1VTIikgJT4lCiAgcmVuYW1lKEFSQSA9IGAxYCwgR0FMID0gYDJgLCBVTE0gPSBgM2AsIExNTSA9IGA0YCwgTUFUID0gYDVgLCAKICAgICAgICAgU0wgPSBgNmAsIFNBID0gYDdgLCBDQyA9IGA4YCwgT1RIRVIgPSBgOWApICU+JQogIHNlbGVjdChMT0NVUywgQVJBLCBHQUwsIFVMTSwgTE1NLCBNQVQsIFNMLCBTQSwgQ0MsIE9USEVSKSAlPiUKICBnYXRoZXIoR3JvdXAsIEhvLCAyOjEwKQoKIyBjb21wYXJlIG9ic2VydmVkIGhldGVyb3p5Z29zaXR5IHBlciBsb2N1cyBhbmQgY2x1c3RlcgpnZ3Bsb3QoSG9fcGVyLCBhZXMoeCA9IEdyb3VwLCB5ID0gSG8pKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIGxhYnMoeCA9ICJHcm91cCIsIHkgPSAib2JzZXJ2ZWQgaGV0ZXJvenlnb3NpdHkgSG8iKSArCiAgdGhlbWVfc3RhbmRhcmQKCmBgYAoKQ29tcGFyZSBkaXN0cmlidXRpb24gb2YgZXhwZWN0ZWQgaGV0ZXJvenlnb3NpdHkgSGUsICh3aXRoaW4gY2x1c3RlciBkaXZlcnNpdHkpIGluIGVhY2ggcHV0YXRpdmUgcG9wdWxhdGlvbjoKCmBgYHtyfQoKIyB3aXRoaW4gY2x1c3RlciBkaXZlcnNpdHkvZXhwIGRpdmVyc2l0eSBwZXIgbG9jdXMgKHJvd3MpIGFuZCBjbHVzdGVyIChjb2x1bW5zKQpIc19wZXIgPC0gYXMuZGF0YS5mcmFtZShHZW5EaXYyJEhzKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oIkxPQ1VTIikgJT4lCiAgcmVuYW1lKEFSQSA9IGAxYCwgR0FMID0gYDJgLCBVTE0gPSBgM2AsIExNTSA9IGA0YCwgTUFUID0gYDVgLCAKICAgICAgICAgU0wgPSBgNmAsIFNBID0gYDdgLCBDQyA9IGA4YCwgT1RIRVIgPSBgOWApICU+JQogIHNlbGVjdChMT0NVUywgQVJBLCBHQUwsIFVMTSwgTE1NLCBNQVQsIFNMLCBTQSwgQ0MsIE9USEVSKSAlPiUKICBnYXRoZXIoR3JvdXAsIEhzLCAyOjEwKQoKIyBjb21wYXJlIGV4cGVjdGVkIGhldGVyb3p5Z29zaXR5IHBlciBsb2N1cyBhbmQgY2x1c3RlcgpnZ3Bsb3QoSHNfcGVyLCBhZXMoeCA9IEdyb3VwLCB5ID0gSHMpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIGxhYnMoeCA9ICJHcm91cCIsIHkgPSAiZXhwZWN0ZWQgaGV0ZXJvenlnb3NpdHkgSG8iKSArCiAgdGhlbWVfc3RhbmRhcmQKCmBgYAoKQ29tcGFyaXNvbiBvZiBvYmVydmVkIHZzLiBoZXRlcm96eWdvc2l0eSB3aXRoaW4gZWFjaCBwb3B1bGF0aW9uOgoKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKIyBqb2luIGRhdGEgZnJhbWVzCkhldF9wZXIgPC0gbGVmdF9qb2luKEhzX3BlciwgSG9fcGVyKQoKIyBwbG90IHBlciBjbHVzdGVyCmdncGxvdChIZXRfcGVyLCBhZXMoeCA9IEhvLCB5ID0gSHMpKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDE5LCBzaXplID0gMSkgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSwgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICB4bGltKDAsIDEpICsKICB5bGltKDAsIDEpICsKICBmYWNldF9ncmlkKC4gfiBHcm91cCkgKwogIGxhYnMoeCA9ICJvYnNlcnZlZCBoZXRlcm96eWdvc2l0eSBIbyIsIHkgPSAid2l0aGluIGNsdXN0ZXIgZGl2ZXJzaXR5IEhzIChIZXhwKSIpICsKICB0aGVtZV9zdGFuZGFyZAoKYGBgCgpUZXN0IGZvciBkZXZpYXRpb25zIGZyb20gSGFyZHktV2VpbmJlcmcgZXF1aWxpYnJpdW0gd2l0aGluIGVhY2ggZ3JvdXAgKFBPUCBsZXZlbCk6CgpgYGB7cn0KCiMgc2V0UG9wKGdlbikgPC0gflBPUAojIAojIHBvcE5hbWVzKGdlbikKIyAKIyAjIHNob3VsZCBiZSBhYmxlIHRvIHVzZSBsYXBwbHkKIyBIV0UgPC0gc2VwcG9wKGdlbikgJT4lIAojICAgIGxhcHBseShody50ZXN0LCBCPTEwMDApCiMgIAojIEhXRVtbIkFMTCJdXSA8LSBody50ZXN0KGdlbiwgQiA9IDEwMDApCiMgIAojICMgY29udmVydCB0byBkYXRhLmZyYW1lcwojIGh3ZSA8LSBsaXN0KCkKIyAgCiMgZm9yIChtIGluIG5hbWVzKEhXRSkpIHsKIyAgICBod2VbW21dXSA8LSBhcy5kYXRhLmZyYW1lKEhXRVtbbV1dKSAlPiUKIyAgICByZW5hbWUocHZhbCA9IFByLmV4YWN0KSAlPiUKIyAgICByb3duYW1lc190b19jb2x1bW4oIkxPQ1VTIikgJT4lCiMgICAgc2VsZWN0KExPQ1VTLCBwdmFsKQojIH0KIyAgCiMgaHdlIDwtIGxkcGx5KGh3ZSwgZGF0YS5mcmFtZSkgJT4lCiMgICAgcmVuYW1lKFBPUCA9IGAuaWRgKSAlPiUKIyAgICBtdXRhdGUoSFdFID0gaWZlbHNlKHB2YWwgPD0gMC4wNSwgIk9VVCIsICJJTiIpKQojIAojIHdyaXRlX2RlbGltKGh3ZSwgInJlc3VsdHMvSFdFYnlwb3AiLCBkZWxpbSA9ICJcdCIpCgpod2UgPC0gcmVhZF9kZWxpbSgicmVzdWx0cy9IV0VieXBvcCIsIGRlbGltID0gIlx0IikKCmh3ZV9wZXJwb3AgPC0gaHdlICU+JQogIGdyb3VwX2J5KFBPUCkgJT4lCiAgY291bnQoSFdFKQoKaHdlX3BlcmxvYyA8LSBod2UgJT4lCiAgZmlsdGVyKFBPUCAhPSAiQUxMIikgJT4lCiAgZ3JvdXBfYnkoTE9DVVMpICU+JQogIGNvdW50KEhXRSkgJT4lCiAgZmlsdGVyKEhXRSA9PSAiT1VUIikKCmBgYAoKRm9ybWF0IG91dHB1dCBhbmQgdmlldyByZXN1bHRzOgoKYGBge3IgSFdFIHJlc3VsdHMsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTEyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKIyBwbG90IGRhdGEKZ2dwbG90KGh3ZV9wZXJwb3AsIGFlcyh4ID0gSFdFLCB5ID0gbikpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgbGFicyh4ID0gInNpZ25pZmljYW50IGRldmlhdGlvbiBmcm9tIEhXRSAocCA+IDAuMDUpIiwgeSA9ICJudW1iZXIgb2YgbG9jaSIpICsKICBmYWNldF9ncmlkKCB+IFBPUCkgKwogIHRoZW1lX3N0YW5kYXJkCgpgYGAKCkRpc3RyaWJ1dGlvbiBvZiBudW1iZXIgb2YgdGltZXMgbG9jaSBhcmUgb3V0IG9mIEhXRQoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NH0KCmdncGxvdChod2VfcGVybG9jLCBhZXMoeCA9IG4pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBsYWJzKHggPSAibm8gcG9wdWxhdGlvbnMgb3V0IG9mIEhXRSIsIHkgPSAibm8uIGxvY2kiKSArCiAgdGhlbWVfc3RhbmRhcmQKCmBgYAoKUmVtb3ZlIGxvY2kgb3V0IG9mIEhXRSBpbiBldmVyeSBwb3B1bGF0aW9uOgoKYGBge3J9CgojIHJlbW92ZSBmbGFnZ2VkIGxvY2kKcmVtb3ZlbG9jIDwtIGh3ZV9wZXJsb2MgJT4lCiAgZmlsdGVyKG4gPj0gNSkKCmdlbiA8LSBnZW5pbmQucmVtLmxvY2koZ2VuLCByZW1vdmVsb2MkTE9DVVMpCgpnZW4KCmBgYAoKIyBGaW5hbCBkYXRhIHNldAoKV3JpdGUgZmlsZSB3aXRoIGZpbHRlcmVkIGRhdGEgc2V0OgoKYGBge3Igd3JpdGUgZmlsdGVyZWQgZGF0YSBzZXR9CgpsaWJyYXJ5KHJhZGlhdG9yKQoKc2V0UG9wKGdlbikgPC0gflBPUAoKIyBjb252ZXJ0IHRvIHRpZHkgZGF0YSBzZXQKdGlkeSA8LSBnZW5vbWljX2NvbnZlcnRlcihkYXRhID0gZ2VuKQoKdGlkeV9nZW4gPC0gdGlkeSR0aWR5LmRhdGEKCndyaXRlX2RlbGltKHRpZHlfZ2VuLCAiZGF0YS9QT1BHRU4vQ0xFLnRpZHkuZ2Vub3R5cHllcyIsIGRlbGltID0gIlx0IikKCiMgaW1wb3J0IHRvIGNvbnZlcnQgYW5kIHdyaXRlIG91dCBhcmxlcXVpbiBhbmQgZ2VuZXBvcCBmb3JtYXR0ZWQgZmlsZXMKdGlkeSA8LSByZWFkX2RlbGltKCJkYXRhL1BPUEdFTi9DTEUudGlkeS5nZW5vdHlweWVzIiwgZGVsaW0gPSAiXHQiKQoKZ2VuIDwtIGdlbm9taWNfY29udmVydGVyKGRhdGEgPSB0aWR5LCBvdXRwdXQgPSBjKCJhcmxlcXVpbiIsICJnZW5lcG9wIiwgImdlbmluZCIpLCBmaWxlbmFtZSA9ICJkYXRhL1BPUEdFTi9DTEUiKQoKIyBjcmVhdGUgZ2VuaW5kIG9iamVjdApnZW4gPC0gZ2VuJGdlbmluZC5uby5pbXB1dGF0aW9uCgpnZW4KCmBgYAoKV3JpdGUgZmlsZXMgd2l0aCBpbmRpdmlkdWFscyBhbmQgQ29udGlncyBzdGlsbCBjb250YWluZWQgaW4gZGF0YSBzZXQgYW5kIHVzZSB0byBmaWx0ZXIgdmNmIGZpbGUuCgpgYGB7cn0KCmtlZXBsb2NpIDwtIGFzLmRhdGEuZnJhbWUobG9jTmFtZXMoZ2VuKSkgJT4lCiAgcmVuYW1lKExPQ1VTID0gYGxvY05hbWVzKGdlbilgKQoKa2VlcGxvY2kgPC0gZmlsdGVyKGxvY19zdGF0c19GMTQsIENIUiAlaW4lIGtlZXBsb2NpJExPQ1VTKSAlPiUKICBzZWxlY3QoQ0hSLCBQT1MpCgp3cml0ZS50YWJsZShrZWVwbG9jaSwgImRhdGEvVkNGL2ZpbHRlcmVkLmxvY2kiLAogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UpCgprZWVwaW5kIDwtIGFzLmRhdGEuZnJhbWUoaW5kTmFtZXMoZ2VuKSkgJT4lCiAgcmVuYW1lKExJQl9JRCA9IGBpbmROYW1lcyhnZW4pYCkgJT4lCiAgc2VwYXJhdGUoTElCX0lELCBpbnRvID0gYygiU1AiLCAiTElCIiwgIldFTEwiLCAiU0FNUExFX0lEIiksIHNlcCA9ICItIiwgZXh0cmEgPSAibWVyZ2UiKSAlPiUKICBzZXBhcmF0ZShTQU1QTEVfSUQsIGludG8gPSBjKCJQMSIsICJQMiIpLCBzZXAgPSAiLSIsIHJlbW92ZSA9IFRSVUUpICU+JQogIHVuaXRlKFNBTVBMRV9JRCwgNDo1LCBzZXAgPSAiXyIpICU+JQogIHVuaXRlKExJQldFTEwsIDI6Mywgc2VwID0gIi0iKSAlPiUKICB1bml0ZShMSUJfSUQsIDE6Mywgc2VwID0gIl8iKQogIAprZWVwaW5kIDwtZmlsdGVyKGluZF9zdGF0c19GMTQsIElORFYgJWluJSBrZWVwaW5kJExJQl9JRCkgJT4lCiAgc2VsZWN0KElORFYpCgp3cml0ZS50YWJsZShrZWVwaW5kLCAiZGF0YS9WQ0YvZmlsdGVyZWQuaW5kIiwKICAgICAgICAgICAgY29sLm5hbWVzID0gRkFMU0UsIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFKQoKYGBgCgpXcml0ZSB2Y2YgZmlsZSBhbmQgMDEyIGZvcm1hdDoKCmBgYHtiYXNofQoKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUuRjE0LnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvdGVtcC9DTEUgLS1wb3NpdGlvbnMgZGF0YS9WQ0YvZmlsdGVyZWQubG9jaSAtLWtlZXAgZGF0YS9WQ0YvZmlsdGVyZWQuaW5kIC0tcmVjb2RlIC0tcmVjb2RlLUlORk8tYWxsCgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5yZWNvZGUudmNmIC0tb3V0IGRhdGEvUE9QR0VOL0NMRSAtLTAxMgoKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuZmlsIC0tZGVwdGgKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuZmlsIC0tc2l0ZS1tZWFuLWRlcHRoCnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLmZpbCAtLW1pc3NpbmctaW5kdgp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5maWwgLS1taXNzaW5nLXNpdGUKdmNmdG9vbHMgLS12Y2YgZGF0YS9WQ0YvdGVtcC9DTEUucmVjb2RlLnZjZiAtLW91dCBkYXRhL1ZDRi9DTEUuZmlsIC0taGV0CnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLmZpbCAtLWhhcmR5CnZjZnRvb2xzIC0tdmNmIGRhdGEvVkNGL3RlbXAvQ0xFLnJlY29kZS52Y2YgLS1vdXQgZGF0YS9WQ0YvQ0xFLmZpbCAtLXNpdGUtcXVhbGl0eQp2Y2Z0b29scyAtLXZjZiBkYXRhL1ZDRi90ZW1wL0NMRS5yZWNvZGUudmNmIC0tb3V0IGRhdGEvVkNGL0NMRS5maWwgLS1nZW5vLWRlcHRoCgpgYGAKCkNvbXBhcmUgc3RhdHMgZm9yIGZpbmFsIGZpbHRlcmVkIGRhdGEgc2V0OgogICAgCmBgYHtyIHN0YXRzIGZpbmFsLCBmaWcuaGVpZ2h0PTIwLCBmaWcud2lkdGg9MTAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIGxvYWQgc3RhdHMgZmlsZXMgLS0tLQppbmRfc3RhdHNfZmlsIDwtIHJlYWQuaW5kLnN0YXRzKGRpciA9ICJkYXRhL1ZDRiIsIHZjZiA9ICJDTEUuZmlsIikgJT4lCiAgc2VwYXJhdGUoSU5EViwgaW50byA9IGMoIlNQIiwgIkxJQiIsICJTQU1QTEVfSUQiKSwgc2VwID0gIl8iLCByZW1vdmUgPSBGQUxTRSkKCmxvY19zdGF0c19maWwgPC0gcmVhZC5sb2Muc3RhdHMoZGlyID0gImRhdGEvVkNGLyIsIHZjZiA9ICJDTEUuZmlsIikKClZpZXcoaW5kX3N0YXRzX2ZpbCkKVmlldyhoYXBfaW5kX3N0YXRzKQoKIyBwbG90IG1pc3NpbmcgZGF0YSBwZXIgaW5kdiAtLS0tCnAxIDwtIGdncGxvdChpbmRfc3RhdHNfZmlsLCBhZXMoeCA9IE1JU1NfQ0xFLmZpbCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IC4wMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImRhcmtvcmFuZ2UiKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oTUlTU19DTEUuZmlsLCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAwLjI1KSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIm1pc3NpbmcgZGF0YSBwZXIgaW5kdiIpICsKICB0aGVtZV9zdGFuZGFyZAoKIyBwbG90IEZpcyBwZXIgaW5kdiAtLS0tCnAyIDwtIGdncGxvdChpbmRfc3RhdHNfZmlsLCBhZXMoeCA9IEZpc19DTEUuZmlsKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gLjAxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihGaXNfQ0xFLmZpbCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMCksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJGaXMgcGVyIGluZHYiKSArCiAgdGhlbWVfc3RhbmRhcmQKCiMgcGxvdCByZWFkIGRlcHRoIHBlciBpbmR2IC0tLS0KcDMgPC0gZ2dwbG90KGluZF9zdGF0c19maWwsIGFlcyh4ID0gTUVBTl9ERVBUSF9DTEUuZmlsKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMTAsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKE1FQU5fREVQVEhfQ0xFLmZpbCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMjApLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAibWVhbiByZWFkIGRlcHRoIHBlciBpbmR2IikgKwogIHRoZW1lX3N0YW5kYXJkCgojIHBsb3QgZGVwdGggdnMgbWlzc2luZyAtLS0tCnA0IDwtIGdncGxvdChpbmRfc3RhdHNfZmlsLCBhZXMoeCA9IE1FQU5fREVQVEhfQ0xFLmZpbCwgeSA9IE1JU1NfQ0xFLmZpbCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKE1FQU5fREVQVEhfQ0xFLmZpbCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMjApLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWVhbihNSVNTX0NMRS5maWwsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IDAuMjUpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAibWVhbiBkZXB0aCBwZXIgaW5kdiIsIHkgPSAiJSBtaXNzaW5nIGRhdGEiKSArCiAgdGhlbWVfc3RhbmRhcmQKCiMgcGxvdCBGaXMgdnMgbWlzc2luZyBkYXRhIHBlciBpbmR2IC0tLS0KcDUgPC0gZ2dwbG90KGluZF9zdGF0c19maWwsIGFlcyh4ID0gRmlzX0NMRS5maWwsIHkgPSBNSVNTX0NMRS5maWwpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihGaXNfQ0xFLmZpbCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMCksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtZWFuKE1JU1NfQ0xFLmZpbCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMC4yNSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJGaXMgcGVyIGluZHYiLCB5ID0gIiUgbWlzc2luZyBkYXRhIikgKwogIHRoZW1lX3N0YW5kYXJkCgojIHBsb3QgRmlzIHZzIG1lYW4gZGVwdGggcGVyIGluZHYgLS0tLQpwNiA8LSBnZ3Bsb3QoaW5kX3N0YXRzX2ZpbCwgYWVzKHggPSBGaXNfQ0xFLmZpbCwgeSA9IE1FQU5fREVQVEhfQ0xFLmZpbCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKEZpc19DTEUuZmlsLCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAwKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1lYW4oTUVBTl9ERVBUSF9DTEUuZmlsLCBuYS5ybSA9IFRSVUUpKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSAyMCksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJGaXMgcGVyIGluZHYiLCB5ID0gIm1lYW4gZGVwdGggcGVyIGluZHYiKSArCiAgdGhlbWVfc3RhbmRhcmQKCiMgcGxvdCBkaXN0cmlidXRpb24gbWlzc2luZyBkYXRhIHBlciBsb2N1cyAtLS0tCnA3IDwtIGdncGxvdChpbmRfc3RhdHNfZmlsLCBhZXMoeCA9IE1JU1NfQ0xFLmZpbCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMDEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKE1JU1NfQ0xFLmZpbCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMC4xKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIiUgbWlzc2luZyBkYXRhIHBlciBsb2N1cyIpICsKICB0aGVtZV9zdGFuZGFyZAoKIyBwbG90IGRpc3RyaWJ1dGlvbiBtZWFuIHJlYWQgZGVwdGggLS0tLQpwOCA8LSBnZ3Bsb3QobG9jX3N0YXRzX2ZpbCwgYWVzKHggPSBNRUFOX0RFUFRIX0NMRS5maWwpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1LCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZGFya29yYW5nZSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihNRUFOX0RFUFRIX0NMRS5maWwsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IDIwKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIm1lYW4gcmVhZCBkZXB0aCBwZXIgbG9jdXMiKSArCiAgdGhlbWVfc3RhbmRhcmQKCiMgcGxvdCByZWFkIGRlcHRoIHZzIG1pc3NpbmcgZGF0YSAtLS0tCnA5IDwtIGdncGxvdChsb2Nfc3RhdHNfZmlsLCBhZXMoeCA9IE1FQU5fREVQVEhfQ0xFLmZpbCwgeSA9IE1JU1NfQ0xFLmZpbCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKE1FQU5fREVQVEhfQ0xFLmZpbCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMjApLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWVhbihNSVNTX0NMRS5maWwsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IDAuMSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogIGxhYnMoeCA9ICJtZWFuIGRlcHRoIHBlciBsb2N1cyIsIHkgPSAiJSBtaXNzaW5nIGRhdGEiKSArCiAgdGhlbWVfc3RhbmRhcmQKCiMgcGxvdCBubyBvZiBTTlBzIHBlciBsb2N1cyAtLS0tCnAxMCA8LSBsb2Nfc3RhdHNfZmlsICU+JQogIGNvdW50KENIUikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJkYXJrb3JhbmdlIikgKyAKICBsYWJzKHggPSAibnVtYmVyIG9mIFNOUHMgcGVyIGxvY3VzIikgKwogIHRoZW1lX3N0YW5kYXJkCgp0ZW1wIDwtIGxvY19zdGF0c19maWwgJT4lCiAgY291bnQoQ0hSKQoKIyBwbG90IG51bWJlciBvZiBTTlBzIHBlciBjb250aWcgdnMuIG1lYW4gZGVwdGggLS0tLQpwMTEgPC0gbGVmdF9qb2luKHRlbXAsIGxvY19zdGF0c19maWwpICU+JQogIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gbiwgeSA9IE1FQU5fREVQVEhfQ0xFLmZpbCkpICsKICBsYWJzKHggPSAibnVtYmVyIG9mIFNOUHMgcGVyIGNvbnRpZyIsIHkgPSAibWVhbiBkZXB0aCIpICsKICB0aGVtZV9zdGFuZGFyZAoKIyBwbG90IGRlcHRoIHZzIFNOUCBxdWFsaXR5IC0tLS0Kc2l0ZV9xdWFsIDwtIHJlYWQudGFibGUoImRhdGEvVkNGL0NMRS5maWwubHF1YWwiLCAKICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUKICBtdXRhdGUoUFJPQiA9IDEwXigtUVVBTC8xMCkpCgp0ZW1wIDwtIGRhdGEuZnJhbWUobG9jX3N0YXRzX2ZpbCRNRUFOX0RFUFRIX0NMRS5maWwsIHNpdGVfcXVhbCRRVUFMKSAlPiUKICByZW5hbWUoZGVwdGggPSBsb2Nfc3RhdHNfZmlsLk1FQU5fREVQVEhfQ0xFLmZpbCwgcXVhbCA9IHNpdGVfcXVhbC5RVUFMKQoKcDEyIDwtIGdncGxvdCh0ZW1wLCBhZXMoeCA9IGRlcHRoLCB5ID0gcXVhbCkpICsKICBnZW9tX3BvaW50KHNpemUgPSAxKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4oZGVwdGgsIG5hLnJtID0gVFJVRSkpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IDIwKSwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1lYW4ocXVhbCwgbmEucm0gPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMjApLAogICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEpICsKICBsYWJzKHggPSAibWVhbiBkZXB0aCBwZXIgbG9jdXMiLCB5ID0gIlNOUCBxdWFsaXR5IikgKwogIHRoZW1lX3N0YW5kYXJkCgptZmlsIDwtIG11bHRpcGxvdChwMSwgcDIsIHAzLCBwNCwgcDUsIHA2LCBwNywgcDgsIHA5LCBwMTAsIHAxMSwgcDEyLCBjb2xzPTIpCgpgYGAK